3

i have problem with scanf not reading long double in the code below:

(please excuse my poor English)

#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;}
}

here's the input:

2
3.0 0.8660254038 0.8660254038 0.8660254038
657.8256599140 151.6154399062 213.5392629932 139.4878846649

and here what's show in the command line:

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 . . .

I want to know why scanf won't take in long double in the code and how to fix it.

Thanks in advance!

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
minhnhat93
  • 141
  • 1
  • 2
  • 13

5 Answers5

15

Dev-c++ uses MinGW, which uses the gcc compiler and the Microsoft runtime library. Unfortunately, those components disagree on the underlying type to be used for long double (64 vs. 80 or 96 bits, I think). Windows assumes long double is the same size as double; gcc makes long double bigger.

Either choice is valid, but the combination results in a broken C and C++ implementation.

If you don't need the extra range and precision, you can read into a double and store into a long double.

Otherwise, you can write or borrow a custom string to long double converter, or just use a different implementation.

EDIT

More details:

Microsoft's own compiler and runtime library are consistent in treating long double as 64 bits, the same size as double. The language standard permits this (it requires long double to be at least as wide as double, but places the same requirements on both), but it does seem odd that it doesn't take advantage of the x86's 80-bit floating-point hardware.

gcc on x86 treats long double as 96 bits (sizeof (long double) == 12). I think only 80 of those bits are significant; the extra 16 bits are for alignment purposes.

MinGW uses gcc as its compiler, but uses Microsoft's runtime library. For most language features, this works fine, but the mismatch for long double means that you can do computations with long double, but you can't pass long double values (or pointers to them) to the runtime library. It's a bug in MinGW.

There are workarounds within MinGW. You can define the macro __USE_MINGW_ANSI_STDIO, either by passing -D__USE_MINGW_ANSI_STDIO on the gcc command line or by adding a line

#define __USE_MINGW_ANSI_STDIO

to your source files. (It has to be defined before #include <stdio.h>.) A commenter, paulsm4, says that the -ansi and -posix options cause MinGW to use its own conforming library (I have no reason to doubt this, but I'm not currently able to confirm it). Or you can call __mingw_printf() directly.

Assuming you're on Windows, Cygwin might be a good alternative (it uses gcc, but it doesn't use Microsoft's runtime library). Or you can use long double internally, but double for I/O.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • `long double` is whatever the compiler decides it is; an implementation isn't required to use the 80-bit format. The compiler and run-time library decided differently. – Keith Thompson Oct 19 '11 at 16:40
  • No, you made specific examplex. So you are constrained by your specific examples. You aren't speaking of "absolute C". You are speaking of MinGW on Intel Processors. My comment was about your answer, so it was constrained in the same way. – xanatos Oct 19 '11 at 16:42
  • @xanatos: I thought you were disagreeing with my answer. Were you? – Keith Thompson Oct 19 '11 at 16:46
  • It is only 80 bits, but gcc will 128 bit align. – Oscar Korz Oct 19 '11 at 16:51
  • @OscarKorz http://gcc.gnu.org/onlinedocs/gcc-3.4.4/gcc/i386-and-x86_002d64-Options.html `These switches control the size of long double type. The i386 application binary interface specifies the size to be 96 bits, so -m96bit-long-double is the default in 32 bit mode.` but is true on x64 what you said. In the end it's very variable. – xanatos Oct 19 '11 at 17:01
  • @KeithThompson it seems that I need to use long double to get AC on this http://www.spoj.pl/problems/TRICENTR/, i was sure that my formulas are correct but `double` won't get me accepted. I didn't know the problem is so complicated and I have no idea about MinGW or MS runtime library but I read "Conversion specifier of long double in C" (as xanatos suggested) and your explanation seems to be correct. PS: I don't know anything about "custom string to `double` converter" – minhnhat93 Oct 19 '11 at 17:24
  • @KeithThompson Thank for your quick clarification! I appreciate it very much! – minhnhat93 Oct 19 '11 at 17:36
  • @paulsm4: 1) I didn't say the problem is "the compiler". Please reread the first paragraph. 2) Good point. I'll update the answer tomorrow. – Keith Thompson Jun 20 '15 at 08:14
  • Point well taken. To rephrase: 1) the underlying problem is that, by default, Min-GW uses MSVCRTL, which happens to treat a "long double" differently than gcc (MinGW uses gcc as its compiler). 2) ALTERNATE SOLUTION: MinGW defaults to MSVSCRTL (which is faster, but non-conformant). MinGW also provides its own ANSI-compliant library: compile with `-ansi`, `-posix` or `-D__USE_MINGW_ANSI_STDIO`. Or you can explicitly call the ANSI-compliant `__mingw_printf()` directly. – paulsm4 Jun 20 '15 at 19:22
  • @paulsm4: I don't currently have access to MinGW. I'm mildly surprised that the `-ansi` and `-posix` options` cause it to use its own library rather than the MS library. Normally `-ansi` just causes gcc to enforce C89/C90 rules. Can you confirm that? – Keith Thompson Jun 20 '15 at 19:55
  • 1
    Thx. And because of your edit, I was able to change my vote. Thank you again:) – paulsm4 Jun 20 '15 at 20:17
5

You are a lucky lucky man. This won't solve the general problem of long double on MinGW, but I'll explain what is happening to your problem. Now, in a far far day when you'll be able to upvote, I want your upvote. :-) (but I don't want this to be marked as the correct response. It's the response to what you need, but not to what you asked (the general problem in your title scanf not taking in long double) ).

First, the solution: use float. Use %f in scanf/printf. The results comes perfectly equal to the ones given as the solution in your site. As a sidenote, if you want to printf with some decimals, do as it's showed in the last printf: %.10f will print 10 decimals after the decimal separator.

Second: why you had a problem with doubles: the res=sqrt() calculates a square root. Using floats, l*l+r*r-2*cosa*l*r == 0.0, using doubles it's -1.0781242565371940e-010, so something near zero BUT NEGATIVE!!! So the sqrt(-something) is NaN (Not a Number) a special value of double/float. You can check if a number is NaN by doing res != res. This because NaN != NaN (but note that this isn't guaranteed by older C standards, but in many compilers on Intel platform do it. http://www.gnu.org/s/hello/manual/libc/Infinity-and-NaN.html). And this explains why the printf printed something like -1.#IO.

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • I've noticed the problem with `res` and corrected that(`float` gave a value of 0.000 but `double` return a negative result) before i submited the source code to the site. I've used `float`, it gave wrong answer at the 2nd test (`float` gives the area `149604.797` but the correct result is `149604.790`). But I personally think that your speech on NaN is very interesting, definitely informative and valuable. PS: sure I'll upvote you when I can, still need 2 more Rep pts. – minhnhat93 Oct 19 '11 at 18:20
  • @minhnhat93 If you had already found it about the float then I'm not even solving your problem. But note that your problem isn't solvable: http://ideone.com/XZsmP ideone uses gcc on linux. 80 bit long double. Still same result. And note that someone else said (in your page): `yeah case 2 area is wrong, should be .797` – xanatos Oct 19 '11 at 18:58
  • @minhnhat93 I've looked at the problem. spoj.pl/ranks/TRICENTR/lang=CS There are solutions in C#, so the problem can be solved in float or double. But if we consider the number of significative digits they are using, they are using doubles. Simply try to output 0 if res is NaN. – xanatos Oct 19 '11 at 19:35
  • Here's the code I submited to SPOJ: http://ideone.com/XU0uW. It still procedures wrong result so i think there's rounding problem here. I'll try to come up with less complicated computations solution to avoid that whenever possible. – minhnhat93 Oct 20 '11 at 04:35
  • @minhnhat93 cosa*l*r == 2 * b* (b-sqrt(a*a-hb*hb)) / 3 (so you drop `ma` that is a sqrt) – xanatos Oct 20 '11 at 04:39
0

Don't know if this is of use to you but you could have a look at it.

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;
}
Spikatrix
  • 20,225
  • 7
  • 37
  • 83
user80998
  • 48
  • 2
0

You can avoid most of your conversion problems by actually using C++ instead of using legacy C-functions:

#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;
}
Zac Howland
  • 15,777
  • 1
  • 26
  • 42
0

Sadly enough, long double has faulty printing in GCC/Windows. However, you can guarantee that long double still does higher precision calculations in the background when you're doing arithmetic and trigonometry, because it would store at least 80 or 96 bits.

Therefore, I recommend this workaround for various things:

  • Use scanf on doubles, but cast them to long doubles after. You don't need precision in input parsing, anyway.

    double x;
    scanf("%lf", &x); // no biggie
    long double y = x;
    
  • Make sure you use long double versions of functions in the <math.h> library. The normal ones will just cast your long double to double, so the higher precision will become useless.

    long double sy = sqrtl(y);        // not sqrt
    long double ay = 2.0L * acosl(y); // note the L-suffix in the constant
    
  • To print your long double, just cast them back to double and use "%lf". Double can have at most 15 significant digits so it is more than enough. Of course, if it's not enough, you ought to switch to Linux's GCC.

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

    Most programs actually don't need the extra digits for the output. The thing is, even the early digits lose their precision at the slightest use of sqrt or trig functions. THEREFORE, it's OK to keep the double at least just for printing, but what's important is that you still use long double for the rough calculations to not lose the precision you worked hard to invest.

Rico
  • 139
  • 1