5

I'm fairly new to C. I try to write functions for a Vector, but there must be something wrong.
Here's the code:

/* Defines maths for particles. */

#include <math.h>
#include <stdio.h>

/* The vector struct. */
typedef struct {
    long double x, y, z;
} Vector;

Vector Vector_InitDoubleXYZ(double x, double y, double z) {
    Vector v;
    v.x = (long double) x;
    v.y = (long double) y;
    v.z = (long double) z;
    return v;
}

Vector Vector_InitDoubleAll(double all) {
    Vector v;
    v.x = v.y = v.z = (long double) all;
    return v;
}


Vector Vector_InitLongDXYZ(long double x, long double y, long double z) {
    Vector v;
    v.x = x;
    v.y = y;
    v.z = z;
    return v;
}

Vector Vector_InitLongDAll(long double all) {
    Vector v;
    v.x = v.y = v.z = all;
    return v;
}

Vector Vector_AddVector(Vector *v1, Vector *v2) {
    Vector v3;
    v3.x = v1->x + v2->x;
    v3.y = v1->y + v2->y;
    v3.z = v1->z + v2->z;
    return v3;
}

Vector Vector_AddDouble(Vector *v1, double other) {
    Vector v2;
    v2.x = v1->x + other;
    v2.y = v1->y + other;
    v2.z = v1->z + other;
    return v2;
}

Vector Vector_AddLongD(Vector *v1, long double other) {
    Vector v2;
    v2.x = v1->x + other;
    v2.y = v1->y + other;
    v2.z = v1->z + other;
    return v2;
}

void Vector_Print(Vector *v) {
    printf("X: %Lf, Y: %Lf, Z: %Lf\n", v->x, v->y, v->z); //Before edit: used %ld
}

double Vector_Length(Vector *v) {
    return pow(pow(v->x, 2) + pow(v->y, 2) + pow(v->z, 2), 0.5);
}



int main() {
    Vector v = Vector_InitDoubleXYZ(2.0, 1.0, 7.0); //Before edit: (2.0d, 1.0d, 7.0d);

    Vector_Print(&v);
}

I'm using gcc to compile. Running vector.exe in the commandline gives me the following output:

X: 0, Y: -2147483648, Z: 9650176

and I do not understand why this is happening.

I appreciate any hints (even about my coding-style or whatever could've be done better in the code).
Thank you,

Update: Using the MSVC Compiler works just fine, it seems to be an issue of gcc. Do you know why this happens ?

Niklas R
  • 16,299
  • 28
  • 108
  • 203
  • Works fine for me (gcc 4.5.2) once I use `%Lg` (or `%Lf`) instead of `%ld` in the `printf`. (And no, `%d` does not mean "double"; it means "int". Go figure.) Your original code is undefined. If it works on MSVC, that is because MSVC is doing something outside of the standard. – Nemo Aug 20 '11 at 21:43
  • I'm sorry, it's working in MSVC with `%Lf` but not with `%d`, didn't correct it. But this still doesn't work with gcc. – Niklas R Aug 20 '11 at 22:02
  • Niklas: Then you need to post the _exact_ code you are now feeding to GCC and indicate which version of GCC and the C library you are using. I am telling you I changed your three `%ld`s to `%Lf` and it works perfectly fine. (I suspect you changed something else.) – Nemo Aug 20 '11 at 22:26
  • @Nemo does the code as it is now work for you ? – Niklas R Aug 20 '11 at 22:27
  • Yes, it works fine. It also looks fine and compiles with no warnings. It prints `X: 2.000000, Y: 1.000000, Z: 7.000000`. Are you sure you are actually compiling and running the new code? – Nemo Aug 20 '11 at 22:29
  • `d` does not mean `int`, it means "decimal". As opposed to hex (`x`) or octal (`o`). – R.. GitHub STOP HELPING ICE Aug 21 '11 at 04:03
  • [can't print correctly a long double in C](http://stackoverflow.com/q/26296058/995714) – phuclv Sep 21 '15 at 07:47

5 Answers5

13

The problem (after fixing the various problems if using integer specifiers for floating point formatting) is that you're mixing GCC types with an MSVC runtime that doesn't understand them.

First off, MinGW is a GCC compiler, but it uses an MSVC runtime for the bulk of it runtime support. What this means for the printf() family of functions is that only the format specifiers that msvcrt.dll supports and only the types that msvcrt.dll supports will work. But GCC doesn't know anything about this, so it'll pass its own types and, of course, the format specifiers are whatever you pass in the format string (though GCC might issue warnings that don't really apply to the msvcrt.dll situation). See Strange "unsigned long long int" behaviour for some examples based on 64-bit ints (I think that newer versions of msvcrt.dll may have fixed some or all of the 64-bit int issues though).

The other part of this problem you're running into is that long double in GCC is a different type than long double in MSVC. GCC uses a 96-bit or 128-bit type for long double on x86 or x64 targets (see http://gcc.gnu.org/onlinedocs/gcc/i386-and-x86_002d64-Options.html). However, MSVC uses a 64-bit type - basically long double is exactly the same as double for msvcrt.dll (http://msdn.microsoft.com/en-us/library/9cx8xs15.aspx):

Previous 16-bit versions of Microsoft C/C++ and Microsoft Visual C++ supported the long double, 80-bit precision data type. In Win32 programming, however, the long double data type maps to the double, 64-bit precision data type. The Microsoft run-time library provides long double versions of the math functions only for backward compatibility. The long double function prototypes are identical to the prototypes for their double counterparts, except that the long double data type replaces the double data type. The long double versions of these functions should not be used in new code.

So what this boils down to is that the GCC/MinGW long double type will simply not be compatible with the formatted I/O in msvcrt.dll. Either switch to using double with MinGW, or if you need to use long double you'll have to cast the values to (double) for formatted I/O or come up with your own formatting routines.

Another option might be to use the GCC compiler under Cygwin, which I think will avoid relying on msvcrt.dll for I/O formatting (at the cost of relying on the Cygwin environment).

Community
  • 1
  • 1
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • Wow, great explanation. Now I understand the problem. I'm currently installing Cygwin, thanks ! – Niklas R Aug 21 '11 at 07:29
  • +1 Good job finding the problem with long double == double in MSVC. I haven't notice that. (Though my days of numerical programming, where long double precision matters, where mostly on gcc-based compilers.) – Tomek Szpakowicz Aug 21 '11 at 08:14
  • One note: AFAIK 96- or 128-bit size of gcc for x86 and x64 is just a matter of alignment. Arithmetic on those types is still in 80-bit precision of x86 floating point registers. Just in case you really cared about precision. – Tomek Szpakowicz Aug 21 '11 at 08:26
  • This definitely needs more upvotes. I was wondering why literally printing `1000.00L;` was printing `1.13263e-317`. – chris Mar 27 '12 at 01:23
  • 96 or 128 bits are the memory allocated to long doubles variables only. Long doubles are still 80-bit wide – phuclv Aug 08 '13 at 01:09
  • Suggest mentioning that in mingw-w64 , you can compile with `-D__USE_MINGW_ANSI_STDIO=1` and then it will use its own working implementation instead of deferring to MS leading to the bug. (IDK whether the old mingw also supports that, but one poster said that it didn't work for them when they tried) – M.M Apr 15 '16 at 05:01
7

Your format string doesn't match your type. %ld is a format specifier for a long int, not a long double. Use %Lg.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • Actually, you need to use `%Lg` (I think) for `long double`. – Chris Lutz Aug 20 '11 at 20:45
  • The output is even more weird. `X: -0, Y: 2.37177e-317, Z: -1#QNAN` Using `%lf` does also not work. – Niklas R Aug 20 '11 at 20:47
  • @Niklas R, maybe you have a weird `printf` implementation? Using `%Lg` works fine for me here. @Chris - thanks - I was already editing that in =). – Carl Norum Aug 20 '11 at 20:47
  • Weird. It's the MinGW gcc compiler. And where the heck does this `#QNAN` come from ? – Niklas R Aug 20 '11 at 20:49
  • 1
    @Niklas - what are the `d` suffixes for? Maybe those are related? Neither my `gcc` nor `clang` understand it. A floating point constant is a `double` by default in C, and the only valid suffixes are `f`, `l`, `F`, and `L`, according to the spec. – Carl Norum Aug 20 '11 at 20:51
  • @Carl Ehm.. Doesnt it indicate it's a double ??! xD – Niklas R Aug 20 '11 at 20:52
  • @Niklas, no I don't think so - I think it's just invalid C. – Carl Norum Aug 20 '11 at 20:53
  • 1
    @Niklas: MinGW uses the Microsoft C++ runtime, which means you have to use the Microsoft syntax, which only accepts `%lf` or `%Lf` to print a `long double`, not `%le` or `%lg`. See [MSDN](http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx). – Adam Rosenfield Aug 20 '11 at 20:54
  • @Adam Doesn't work either :( @Carl Leaving out the `d`'s doesn't change anything .. :/ – Niklas R Aug 20 '11 at 20:56
  • @Niklas, maybe `long double` is the problem; try with just regular `double` and see if that makes it go away. Make sure to update your `printf` format too. If that fixes it, you're going to need more documentation about how `long double` works in your environment, since it seems the standard behaviour isn't implemented for you. – Carl Norum Aug 20 '11 at 20:59
  • @Carl I'll try it. But using the msvc compiler works just fine. – Niklas R Aug 20 '11 at 21:01
  • Lol. Why the heck does gcc mess it up? Maybe i should reinstall gcc ? – Niklas R Aug 20 '11 at 21:04
  • @CarlNorum let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/2698/discussion-between-niklas-r-and-carl-norum) – Niklas R Aug 20 '11 at 21:04
2

Should be:

void Vector_Print(Vector *v) {
    printf("X: %Lf, Y: %Lf, Z: %Lf\n", v->x, v->y, v->z);
}

With f (or g or e or G or E) for floating point type and with uppercase L for long double.

That's standard C and C++ specifier for long double. Using lowercase l might work in some implementations, but why make your program less portable then necessary.


Note: With scanf functions family, specifiers are slightly different. %f (and others) means float, %lf means double and %Lf means long double.

With printf there is no specifier for float. You need to cast float variables to double.


Note 2: Apparently mingw has some incompatibilities caused by using MSVC runtime that cause problems with %Lf. It's strange because normally MSVC allows both %lf and %Lf.

Mingw has alternative implementation of stdio. You can enable it by #defining __USE_MINGW_ANSI_STDIO to 1 (see). I can't promise it'll help. I don't know mingw too well.

Update: Michael Burr's answer explains why %Lf conversion fails when using MSVC runtime with MinGW. If you still need to use MinGW, try switching to their own implementation. It uses real long double type.

Tomek Szpakowicz
  • 14,063
  • 3
  • 33
  • 55
  • Same output. I'm currently installing Cygwin that may replace gcc for me, I hope it will work there. – Niklas R Aug 20 '11 at 21:24
0

Have you enabled warnings in the compiler? If the compiler gives a warning, it may provide a hint about what is wrong.

Arndt
  • 1
-1

Try something like this:

Vector v;
Vector_InitDoubleXYZ(&v, 2.0d, 1.0d, 7.0d);

where that function is defined as:

void Vector_InitDoubleXYZ(Vector *v, double x, double y, double z) {
long double t;
t = x;
v->x = t;
t=y;
v->y = t;
t=z;
v->z = t;
}
Foo Bah
  • 25,660
  • 5
  • 55
  • 79
  • No, you can return a `struct` by value. It's not efficient, but you can do it. – Chris Lutz Aug 20 '11 at 20:44
  • What does this make better ? I prefer a function that does return an initialized instance. – Niklas R Aug 20 '11 at 20:44
  • @Chris: Actually, thanks to RVO, it is usually just as efficient as passing a pointer... – Nemo Aug 20 '11 at 21:38
  • @Nemo - Not here. RVO is in the C++ standard, but not (to my knowledge) in C. – Chris Lutz Aug 20 '11 at 21:45
  • @Chris: Yes here. Most (all?) optimizing C compilers implement it already, because it does not affect the program's observed behavior. The only reason RVO gets a special shout-out in the C++ spec is because it modifies observed behavior (think "copy constructor with side-effects"). There are no copy constructors in C, so the standard has no need to mention it. But any decent compiler will perform it. – Nemo Aug 20 '11 at 22:24
  • @Nemo - Indeed. I'd have thought that would complicate the ABI, but I guess the standard doesn't really mention that either. – Chris Lutz Aug 20 '11 at 23:03