【问题标题】:scanf not taking in long doublescanf 不接受 long double
【发布时间】:2023-04-06 05:28:01
【问题描述】:

我在下面的代码中遇到了 scanf 无法读取 long double 的问题:

(请原谅我的英语不好)

#include <iostream>
#include <cstdlib>
#include <math.h>
using namespace std;
int main()
    {
    int n;
    scanf("%d",&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",&a,&ha,&hb,&hc);//this is where the problem lies,
  //i need to read 4 long double a,ha,hb,hc
        printf("%Lf %Lf %Lf %Lf\n",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 %.3Lf\n",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 以及如何修复它。

提前致谢!

【问题讨论】:

  • 你忘了说明你的问题。
  • 哦,好吧,我想标题很清楚,我想知道为什么scanf不会在代码中花费很长时间跨度>
  • 这不是 C。我建议您对每个源文件使用一种语言:编写多语言源文件不适合胆小的人。
  • 这是dev-c++,我会去掉c标签,谢谢=))
  • 考虑使用 C++ 流 (std::cin &gt;&gt; a &gt;&gt; ha &gt;&gt; hb &gt;&gt; hc) 而不是 C 函数 scanf

标签: c++ mingw


【解决方案1】:

Dev-c++ 使用 MinGW,它使用 gcc 编译器和 Microsoft 运行时库。不幸的是,这些组件在用于long double 的基础类型上存在分歧(我认为是 64 位与 80 位或 96 位)。 Windows 假定 long doubledouble 大小相同; gcc 使long double 更大。

任何一个选择都有效,但组合会导致 C 和 C++ 实现中断。

如果您不需要额外的范围和精度,可以读入double 并存储到long double

否则,您可以编写或借用自定义字符串到long double 转换器,或者只是使用不同的实现。

编辑

更多细节:

Microsoft 自己的编译器和运行时库在将long double 视为64 位,与double 大小相同方面是一致的。语言标准允许这样做(它要求long double至少double一样宽,但对两者的要求相同),但它没有利用这一点似乎很奇怪x86 的 80 位浮点硬件。

x86 上的 gcc 将 long double 视为 96 位 (sizeof (long double) == 12)。我认为这些位中只有 80 位是重要的。额外的 16 位用于对齐目的。

MinGW 使用 gcc 作为其编译器,但使用 Microsoft 的运行时库。对于大多数语言功能,这可以正常工作,但 long double 的不匹配意味着您可以使用 long double 进行计算,但不能将 long double 值(或指向它们的指针)传递给运行时库。这是 MinGW 中的一个错误。

MinGW 中有一些解决方法。您可以通过在 gcc 命令行上传递 -D__USE_MINGW_ANSI_STDIO 或添加一行来定义宏 __USE_MINGW_ANSI_STDIO

#define __USE_MINGW_ANSI_STDIO

到您的源文件。 (它必须在#include &lt;stdio.h&gt; 之前定义之前。)评论者paulsm4 说-ansi-posix 选项导致MinGW 使用自己的符合库(我没有理由怀疑这个,但我目前无法确认)。或者您可以直接拨打__mingw_printf()

假设您使用的是 Windows,Cygwin 可能是一个不错的选择(它使用 gcc,但不使用 Microsoft 的运行时库)。或者您可以在内部使用 long double,但在 I/O 中使用 double

【讨论】:

  • long double 是编译器决定的;使用 80 位格式不需要实现。编译器和运行时库的决定不同。
  • 不,你做了具体的例子。因此,您受到具体示例的限制。您不是在说“绝对C”。您说的是英特尔处理器上的 MinGW。我的评论是关于你的答案,所以它以同样的方式受到限制。
  • @xanatos:我以为你不同意我的回答。你是吗?
  • 只有80位,但gcc会128位对齐。
  • 谢谢。由于您的编辑,我能够改变我的投票。再次感谢您:)
【解决方案2】:

你是个幸运的幸运儿。这不会解决 MinGW 上 long double 的一般问题,但我会解释你的问题发生了什么。现在,在您可以投票的遥远日子里,我想要您的投票。 :-)(但我不希望这被标记为正确的响应。这是对您需要的响应,而不是对您所要求的响应(标题中的一般问题scanf not taking in long double)。

一、解决方法:使用float。在scanf/printf 中使用%f。结果完全等同于您网站中给出的解决方案。作为旁注,如果你想 printf 带有一些小数,请按照最后一个 printf 中显示的方法进行操作:%.10f 将在小数分隔符后打印 10 个小数。

第二:为什么doubles 有问题:res=sqrt() 计算平方根。使用floats,l*l+r*r-2*cosa*l*r == 0.0,使用doubles 它是-1.0781242565371940e-010,所以接近于零但为负!!!所以sqrt(-something)NaN(不是数字)double/float 的特殊值。您可以通过执行res != res 来检查号码是否为NaN。这是因为NaN != NaN(但请注意,旧的 C 标准不能保证这一点,但在英特尔平台上的许多编译器中都可以做到这一点。http://www.gnu.org/s/hello/manual/libc/Infinity-and-NaN.html)。这就解释了为什么printf 会打印出-1.#IO 之类的东西。

【讨论】:

  • 在我向网站提交源代码之前,我已经注意到res 的问题并纠正了该问题(float 给出的值为 0.000 但double 返回否定结果)。我用过float,它在第二次测试中给出了错误的答案(float 给出了149604.797 的区域,但正确的结果是149604.790)。但我个人认为你关于 NaN 的演讲非常有趣,内容丰富且有价值。 PS:当然我会尽可能地支持你,仍然需要 2 个 Rep pts。
  • @minhnhat93 如果您已经找到了关于浮动的信息,那么我什至无法解决您的问题。但请注意,您的问题无法解决:ideone.com/XZsmP ideone 在 linux 上使用 gcc。 80 位长双。结果还是一样。请注意,其他人(在您的页面中)说:yeah case 2 area is wrong, should be .797
  • @minhnhat93 我已经看到了这个问题。 spoj.pl/ranks/TRICENTR/lang=CS C#中有解决方案,所以问题可以用float或double解决。但是如果我们考虑他们使用的有效数字的数量,他们使用的是双精度数。如果 res 为 NaN,则只需尝试输出 0。
  • 这是我提交给 SPOJ 的代码:ideone.com/XU0uW。它仍然处理错误的结果,所以我认为这里存在舍入问题。我会尽量想出不太复杂的计算解决方案来避免这种情况。
  • @minhnhat93 cosalr == 2 * b* (b-sqrt(aa-hbhb)) / 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", &x); // no biggie
        long double y = x;
        
      • 确保在 库中使用长双精度版本的函数。正常的只会将您的 long double 转换为 double,因此更高的精度将变得无用。

        long double sy = sqrtl(y);        // not sqrt
        long double ay = 2.0L * acosl(y); // note the L-suffix in the constant
        
      • 要打印你的 long double,只需将它们转换回 double 并使用“%lf”。 Double 最多可以有 15 个有效数字,因此绰绰有余。当然,如果还不够,你应该切换到Linux的GCC。

        printf("%.15lf\n", (double) y);
        

        大多数程序实际上不需要额外的数字来输出。问题是,即使是早期的数字只要稍微使用 sqrt 或 trig 函数就会失去精度。因此,至少保留 double 用于打印是可以的,但重要的是您仍然使用 long double 进行粗略计算,以免失去您努力投入的精度。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-30
        相关资源
        最近更新 更多