scanf не принимает длинную двойную

#c #mingw

#c #mingw

Вопрос:

у меня проблема с тем, что scanf не читает long double в приведенном ниже коде:

(пожалуйста, извините за мой плохой английский)

 #include <iostream>
#include <cstdlib>
#include <math.h>
using namespace std;
int main()
    {
    int n;
    scanf("%d",amp;n);
    long double a,b,c,ha,hb,hc,ma,cosa,r,l,res,area;
    for (int i=0;i<n;i  )
    {
        scanf("%Lf %Lf %Lf %Lf",amp;a,amp;ha,amp;hb,amp;hc);//this is where the problem lies,
  //i need to read 4 long double a,ha,hb,hc
        printf("%Lf %Lf %Lf %Lfn",a,ha,hb,hc);//but it returned wrong answer so
  //i used printf to check, ps: the code works with float but not with double or
  //long double
        ha*=3;hb*=3;hc*=3;
        c=(a*ha)/hc; b=(a*ha)/hb;
        ma=sqrt(0.5*b*b 0.5*c*c-0.25*a*a);
        cosa=ha/ma;
        r=(2*ma)/3;
        l=b*(b-sqrt(a*a-hb*hb))/ha;
        res=sqrt(l*l r*r-2*cosa*l*r);
        area=a*ha/2;
        printf("%.3Lf %.3Lfn",area,res);
    }
    system("PAUSE");
    return 0;}
}
  

вот входные данные:

 2
3.0 0.8660254038 0.8660254038 0.8660254038
657.8256599140 151.6154399062 213.5392629932 139.4878846649
  

и вот что отображается в командной строке:

 2
3.0 0.8660254038 0.8660254038 0.8660254038
3.000000 -4824911833695204400000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000.000000 284622047019579100000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000.0
00000 0.866025
-2.000 0.000
657.8256599140 151.6154399062 213.5392629932 139.4878846649
657.825660 -0.000000 28969688850499604000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000.000000 213.539263
-2.000 0.000
Press any key to continue . . .
  

Я хочу знать, почему scanf не принимает long double в коде и как это исправить.

Заранее спасибо!

Комментарии:

1. Вы забыли указать свой вопрос.

2. ну что ж, я думаю, это ясно из названия, я хочу знать, почему scanf не займет много времени в коде

3. Это не C. Я предлагаю вам придерживаться одного языка для каждого исходного файла: написание многоязычных исходных файлов не для слабонервных.

4. это dev-c , я удалю тег c, спасибо =))

5. Рассмотрите возможность использования C streams ( std::cin >> a >> ha >> hb >> hc ) вместо функции C. scanf

Ответ №1:

В Dev-c используется MinGW, который использует компилятор gcc и библиотеку времени выполнения Microsoft. К сожалению, эти компоненты расходятся во мнениях относительно базового типа, который будет использоваться long double (я думаю, 64 против 80 или 96 бит). Windows предполагает long double тот же размер, double что и; gcc делает long double больше.

Любой из вариантов допустим, но комбинация приводит к нарушению реализации на C и C .

Если вам не нужны дополнительные диапазон и точность, вы можете читать в a double и сохранять в a long double .

В противном случае вы можете записать или позаимствовать пользовательскую строку в long double converter или просто использовать другую реализацию.

Редактировать

Подробнее:

Собственный компилятор Microsoft и библиотека времени выполнения согласованы в обработке long double как 64 бита, того же размера, double что и . Стандарт языка допускает это (он должен long double быть как минимум таким же широким, как double , но предъявляет одинаковые требования к обоим), но кажется странным, что он не использует преимущества 80-разрядного оборудования x86 с плавающей запятой.

gcc на x86 обрабатывает long double как 96 бит ( sizeof (long double) == 12 ). Я думаю, что только 80 из этих битов являются значимыми; дополнительные 16 бит предназначены для выравнивания.

MinGW использует gcc в качестве своего компилятора, но использует библиотеку времени выполнения Microsoft. Для большинства языковых функций это работает нормально, но несоответствие для long double означает, что вы можете выполнять вычисления с помощью long double , но вы не можете передавать длинные двойные значения (или указатели на них) в библиотеку времени выполнения. Это ошибка в MinGW.

В MinGW есть обходные пути. Вы можете определить макрос __USE_MINGW_ANSI_STDIO , либо передав -D__USE_MINGW_ANSI_STDIO командную строку gcc, либо добавив строку

 #define __USE_MINGW_ANSI_STDIO
  

к вашим исходным файлам. (Это должно быть определено раньше #include <stdio.h> .) Комментатор paulsm4 говорит, что параметры -ansi and -posix заставляют MinGW использовать свою собственную соответствующую библиотеку (у меня нет причин сомневаться в этом, но в настоящее время я не могу это подтвердить). Или вы можете вызвать __mingw_printf() напрямую.

Предполагая, что вы используете Windows, Cygwin может быть хорошей альтернативой (он использует gcc, но не использует библиотеку времени выполнения Microsoft). Или вы можете использовать long double внутренне, но double для ввода-вывода.

Комментарии:

1. long double это то, что решит компилятор; реализация не требуется для использования 80-битного формата. Компилятор и библиотека времени выполнения решили по-другому.

2. Нет, вы создали конкретный examplex. Итак, вы ограничены вашими конкретными примерами. Вы не говорите об «абсолютном C». Вы говорите о MinGW на процессорах Intel. Мой комментарий касался вашего ответа, поэтому он был ограничен таким же образом.

3. @xanatos: Я думал, вы не согласны с моим ответом. Были ли вы?

4. Это всего 80 бит, но gcc выровняет 128 бит.

5. Спасибо. И из-за вашей правки я смог изменить свой голос. Еще раз спасибо 🙂

Ответ №2:

Вы счастливый счастливчик. Это не решит общую проблему long double с MinGW, но я объясню, что происходит с вашей проблемой. Теперь, в далекий-далекий день, когда вы сможете проголосовать, я хочу, чтобы вы проголосовали. 🙂 (но я не хочу, чтобы это было помечено как правильный ответ. Это ответ на то, что вам нужно, но не на то, что вы просили (общая проблема в вашем названии scanf not taking in long double ) ).

Во-первых, решение: использовать float . Используйте %f в scanf / printf . Результаты полностью совпадают с теми, которые указаны в качестве решения на вашем сайте. В качестве примечания, если вы хотите printf использовать несколько десятичных знаков, сделайте так, как показано в последнем printf: %.10f будет выводить 10 десятичных знаков после десятичного разделителя.

Во-вторых: почему у вас возникла проблема с double s: res=sqrt() вычисляет квадратный корень. Используя float s, l*l r*r-2*cosa*l*r == 0.0, используя double s, это -1.0781242565371940e-010, так что что-то близкое к нулю, НО ОТРИЦАТЕЛЬНОЕ!!! Таким sqrt(-something) образом, это NaN (не число) специальное значение double/float . Вы можете проверить, является ли число NaN , выполнив res != res . Это потому NaN != NaN , что (но обратите внимание, что это не гарантируется более старыми стандартами C, но во многих компиляторах на платформе Intel это делается. http://www.gnu.org/s/hello/manual/libc/Infinity-and-NaN.html ). И это объясняет, почему printf напечатано что-то вроде -1.#IO .

Комментарии:

1. Я заметил проблему res и исправил ее ( float дал значение 0.000, но double вернул отрицательный результат), прежде чем отправить исходный код на сайт. Я использовал float , он дал неправильный ответ на 2-м тесте ( float дает область 149604.797 , но правильный результат 149604.790 ). Но я лично считаю, что ваша речь о NaN очень интересна, определенно информативна и ценна. PS: конечно, я поддержу вас, когда смогу, все равно нужно еще 2 представителя.

2. @minhnhat93 Если вы уже нашли это о float, то я даже не решаю вашу проблему. Но обратите внимание, что ваша проблема неразрешима: ideone.com/XZsmP ideone использует gcc в linux. 80-битный double. Все тот же результат. И обратите внимание, что кто-то еще сказал (на вашей странице): yeah case 2 area is wrong, should be .797

3. @minhnhat93 Я рассмотрел проблему. spoj.pl/ranks/TRICENTR/lang=CS В C # есть решения, поэтому проблему можно решить с помощью float или double. Но если мы рассмотрим количество сигнификативных цифр, которые они используют, они используют удвоения. Просто попробуйте вывести 0, если res равно NaN.

4. Вот код, который я отправил в SPOJ: ideone.com/XU0uW . Он по-прежнему обрабатывает неверный результат, поэтому я думаю, что здесь есть проблема с округлением. Я постараюсь придумать менее сложное решение для вычислений, чтобы избежать этого, когда это возможно.

5. @minhnhat93 cosa l r == 2 * b * (b-sqrt(a a-hb hb)) / 3 (таким образом, вы отбрасываете ma , что это sqrt)

Ответ №3:

Вы можете избежать большинства проблем с преобразованием, фактически используя C вместо использования устаревших C-функций:

 #include <algorithm>
#include <iostream>
#include <iterator>

int main()
{
    long double a = 0.0;

    long double ha = 0.0;
    long double hb = 0.0;
    long double hc = 0.0;
    int n = 0;

    std::cout << "Enter Count:  ";
    std::cin >> n;

    for (int i = 0; i < n; i  )
    {
        std::cout << "Enter A, Ha, Hb, Hc:  ";
        std::cin >> a >> ha >> hb >> hc;
        std::cout.precision(10);
        std::cout << "You Entered:  " 
                  << a  << " " << ha << " " << hb << " " << hc << std::endl;
        ha *= 3;
        hb *= 3;
        hc *= 3;
        long double c = (a * ha) / hc;
        long double b = (a * ha) / hb;
        long double ma = static_cast<long double>(std::sqrt(0.5 * b * b   0.5 * c * c - 0.25 * a * a));
        long double cosa = ha / ma;
        long double r = (2 * ma) / 3;
        long double l = b * (b - static_cast<long double>(std::sqrt(a * a - hb * hb))) / ha;
        long double res = static_cast<long double>(std::sqrt(l * l   r * r - 2 * cosa * l * r));
        long double area = a * ha / 2.0;
        std::cout << "Area = " << area  << std::endl;
    }

    return 0;
}
  

Ответ №4:

Не знаю, полезно ли это для вас, но вы могли бы взглянуть на это.

 long long int XDTOI(long double VALUE)
{
    union
    {
        long double DWHOLE;
        struct
        {
            unsigned int DMANTISSALO:32;
            unsigned int DMANTISSAHI:32;
            unsigned int DEXPONENT:15;
            unsigned int DNEGATIVE:1;
            unsigned int DEMPTY:16;
        } DSPLIT;
    } DKEY;
    union
    {
        unsigned long long int WHOLE;
        struct
        {
            unsigned int ARRAY[2];
        } SPLIT;
    } KEY;
    int SIGNBIT,RSHIFT;
    unsigned long long int BIGNUMBER;
    long long int ACTUAL;

    DKEY.DWHOLE=VALUE; SIGNBIT=DKEY.DSPLIT.DNEGATIVE; 
    RSHIFT=(63-(DKEY.DSPLIT.DEXPONENT-16383));
    KEY.SPLIT.ARRAY[0]=DKEY.DSPLIT.DMANTISSALO;
    KEY.SPLIT.ARRAY[1]=DKEY.DSPLIT.DMANTISSAHI;
    BIGNUMBER=KEY.WHOLE;
    BIGNUMBER>>=RSHIFT;
    ACTUAL=((long long int)(BIGNUMBER));
    if(SIGNBIT==1) ACTUAL=(-ACTUAL);
    return ACTUAL;
}
  

Ответ №5:

К сожалению, у long double неправильная печать в GCC / Windows. Однако вы можете гарантировать, что long double по-прежнему выполняет вычисления с более высокой точностью в фоновом режиме, когда вы выполняете арифметику и тригонометрию, потому что он будет хранить не менее 80 или 96 бит.

Поэтому я рекомендую этот обходной путь для различных вещей:

  • Используйте scanf для удвоений, но после приведите их к длинным удвоениям. В любом случае вам не нужна точность при синтаксическом анализе входных данных.

     double x;
    scanf("%lf", amp;x); // no biggie
    long double y = x;
      
  • Убедитесь, что вы используете длинные двойные версии функций в библиотеке <math.h> . Обычные просто преобразуют ваш длинный double в double, поэтому более высокая точность станет бесполезной.

     long double sy = sqrtl(y);        // not sqrt
    long double ay = 2.0L * acosl(y); // note the L-suffix in the constant
      
  • Чтобы распечатать свой длинный double, просто верните их в double и используйте «%lf». Double может содержать не более 15 значащих цифр, так что этого более чем достаточно. Конечно, если этого недостаточно, вам следует переключиться на Linux GCC.

     printf("%.15lfn", (double) y);
      

    Большинству программ на самом деле не нужны дополнительные цифры для вывода. Дело в том, что даже ранние цифры теряют свою точность при малейшем использовании функций sqrt или trig. ПОЭТОМУ можно сохранить double хотя бы только для печати, но важно то, что вы по-прежнему используете long double для грубых вычислений, чтобы не потерять точность, которую вы усердно вкладывали.