Find the answer to your Linux question:
Results 1 to 9 of 9
Like Tree3Likes
  • 1 Post By Rubberman
  • 1 Post By Rubberman
  • 1 Post By drl
It seems that the C datatypes float, double and long double may be slightly different on different machines. How do I find out what they are for the machines I'm ...
Enjoy an ad free experience by logging in. Not a member yet? Register.
  1. #1
    Just Joined!
    Join Date
    Jul 2010
    Posts
    25

    How do I find float, double, and long double implementation info?


    It seems that the C datatypes float, double and long double may be slightly different on different machines. How do I find out what they are for the machines I'm working on?

    I'm using a 32 node cluster running Gentoo Linux.

  2. #2
    Linux Guru Rubberman's Avatar
    Join Date
    Apr 2009
    Location
    I can be found either 40 miles west of Chicago, in Chicago, or in a galaxy far, far away.
    Posts
    11,380
    The formats of these types will ONLY differ on systems that have either different endian-ness (x86 vs sparc), or word size (32-bit vs. 64-bit systems). How do you tell what the differences are? Usually there will be a compiler macro __BIG_ENDIAN__ or __LITTLE_ENDIAN__ enabled for the processor type. To tell the difference between 32-bit and 64-bit systems, use the sizeof(long) macro function. On a 32-bit system, it should be 4 (bytes, or 32-bits), and for 64-bit systems it should be 8 (bytes, or 64-bits).

    In any case, it should be RARE that you should need to know endian-ness or word-size unless you are doing low-level programming. I normally have to deal with this cruft only if I am writing low-level framework code that has to work identically on all systems, such as generation of hash values that must be byte-equal for the same source string on all systems.
    StupidUser likes this.
    Sometimes, real fast is almost as good as real time.
    Just remember, Semper Gumbi - always be flexible!

  3. #3
    Just Joined!
    Join Date
    Jul 2010
    Posts
    25
    Quote Originally Posted by Rubberman View Post
    The formats of these types will ONLY differ on systems that have either different endian-ness (x86 vs sparc), or word size (32-bit vs. 64-bit systems). How do you tell what the differences are? Usually there will be a compiler macro __BIG_ENDIAN__ or __LITTLE_ENDIAN__ enabled for the processor type. To tell the difference between 32-bit and 64-bit systems, use the sizeof(long) macro function. On a 32-bit system, it should be 4 (bytes, or 32-bits), and for 64-bit systems it should be 8 (bytes, or 64-bits).

    In any case, it should be RARE that you should need to know endian-ness or word-size unless you are doing low-level programming. I normally have to deal with this cruft only if I am writing low-level framework code that has to work identically on all systems, such as generation of hash values that must be byte-equal for the same source string on all systems.
    I didn't know that trick. sizeof(long) returned 8. So I guess it's a 64-bit system (yay!). I also checked sizeof float, double, and long double, because the wikipedia page called "C data types", Basic types section, says that the implementation of long double is not consistent (sizeof(long double) returned 16).

    I'm not really concerned about the endianness of the system, but just for my edification, how do I find which compiler macro is enabled? Where are they located?

  4. #4
    Linux Guru Rubberman's Avatar
    Join Date
    Apr 2009
    Location
    I can be found either 40 miles west of Chicago, in Chicago, or in a galaxy far, far away.
    Posts
    11,380
    Double is by definition 64-bits. A long double is 128-bits (16 bytes). Those values are fixed by definition. Floats are 4 bytes (32-bits). The endian-ness (__BIG_ENDIAN__ vs __LITTLE_ENDIAN__) is defined in (on linux systems) /usr/include/endian.h. Actually, __BIG_ENDIAN__ or __LITTLE_ENDIAN__ are dealt with by the compiler. In the header file /usr/include/endian.h you will find that it looks in /usr/include/bits/endian.h, which on x86 systems will do this
    Code:
    #define __BYTE_ORDER __LITTLE_ENDIAN
    So, if you need to know in your code if you are on a big vs little endian system, do this:
    Code:
    #include <bits/endian.h>
    #if __BYTE_ORDER ==__LITTLE_ENDIAN
    /* Use little endian ordering */
    #else
    /* Use big endian ordering */
    #endif /* __BYTE_ORDER */
    Hope this helps.
    StupidUser likes this.
    Sometimes, real fast is almost as good as real time.
    Just remember, Semper Gumbi - always be flexible!

  5. #5
    drl
    drl is offline
    Linux Engineer drl's Avatar
    Join Date
    Apr 2006
    Location
    Saint Paul, MN, USA / CentOS, Debian, Slackware, {Free, Open, Net}BSD, Solaris
    Posts
    1,283
    Hi.

    I often use the code of S Pemberton at: http://homepages.cwi.nl/~steven/enquire/enquire.c to find details of arithmetic of any box that has a C compiler. For example, many end cases are displayed like:
    Code:
    Smallest x such that 1.0-x != 1.0 = 5.5538256285569977e-17
    After you download the code, you use the non-obvious command:
    Code:
    sh enquire.c
    to cause initial examination of the system, then code is compiled and executed to reveal the details of the arithmetic properties of the system.

    Some 20 years ago the design of hardware was not as standardized as it is now. Most systems now conform to IEEE standards, but there may be compiler options to change some aspects of how code gets translated. Consequently, I keep this around to see how hardware performs at a basic level.

    I just ran this on an AMD64 box (Debian 5), on HP-UX, B.11.11, and on a system running aix 5.1.0.0. The code executed on all systems.

    On a 32-bit vm (guest virtual machine with VMWare) running on a 64-bit host (Xeon chip), one result was:
    Code:
    Only 79 of the 96 bits of a long double are actually used
    It doesn't look like IEEE format
    which might be of use if some calculation didn't seem to be working as normally expected.

    A run on the same host under FreeBSD 8.0 showed:
    Code:
    *** WARNING: Possibly bad output from printf above
        expected value around 2.2204460492503131e-16 ...
    which I have not yet investigated.

    Best wishes ... cheers, drl
    Last edited by drl; 10-23-2012 at 10:41 AM.
    StupidUser likes this.
    Welcome - get the most out of the forum by reading forum basics and guidelines: click here.
    90% of questions can be answered by using man pages, Quick Search, Advanced Search, Google search, Wikipedia.
    We look forward to helping you with the challenge of the other 10%.
    ( Mn, 2.6.n, AMD-64 3000+, ASUS A8V Deluxe, 1 GB, SATA + IDE, Matrox G400 AGP )

  6. #6
    Just Joined!
    Join Date
    Jul 2010
    Posts
    25
    Quote Originally Posted by Rubberman View Post
    The formats of these types will ONLY differ on systems that have either different endian-ness (x86 vs sparc), or word size (32-bit vs. 64-bit systems). How do you tell what the differences are? Usually there will be a compiler macro __BIG_ENDIAN__ or __LITTLE_ENDIAN__ enabled for the processor type. To tell the difference between 32-bit and 64-bit systems, use the sizeof(long) macro function. On a 32-bit system, it should be 4 (bytes, or 32-bits), and for 64-bit systems it should be 8 (bytes, or 64-bits).

    In any case, it should be RARE that you should need to know endian-ness or word-size unless you are doing low-level programming. I normally have to deal with this cruft only if I am writing low-level framework code that has to work identically on all systems, such as generation of hash values that must be byte-equal for the same source string on all systems.
    So (to return to a moderately old post), given that sizeof(long double) returns 16 (as in 16 bytes!), how do I specify the fprintf format specifier to make sure I don't lose any precision? All 16 bytes are not the significand (that is, the significant digits) - some of the bytes are used for the exponent.

    I'm reading documentation (well, I'm reading Wikipedia), but it's kind of confusing and I'm not even certain that I'm reading the right thing.

    If I am reading the right thing, the significand is 113 binary digits. So there are at most 34 decimal digits (floor[(113+1) * log102+1] = 35). Am I right that fprintf format specifier %35.35Le will keep all precision?

    Re-edit: Back to thinking it's 35 digits.
    Edit one more time to reflect correct format specifier per Rubberman's following post.
    Last edited by StupidUser; 11-02-2012 at 01:31 AM.

  7. #7
    Linux Guru Rubberman's Avatar
    Join Date
    Apr 2009
    Location
    I can be found either 40 miles west of Chicago, in Chicago, or in a galaxy far, far away.
    Posts
    11,380
    Quote Originally Posted by StupidUser View Post
    So (to return to a moderately old post), given that sizeof(long double) returns 16 (as in 16 bytes!), how do I specify the fprintf format specifier to make sure I don't lose any precision? All 16 bytes are not the significand (that is, the significant digits) - some of the bytes are used for the exponent.

    I'm reading documentation (well, I'm reading Wikipedia), but it's kind of confusing and I'm not even certain that I'm reading the right thing.

    If I am reading the right thing, the significand is 113 binary digits. So there are at most 35 decimal digits (ceiling[113 * log102] = 35). Am I right that fprintf format specifier %35.35e will keep all precision?
    Dealing with things like long doubles is where the length modifiers come in for the format string. From the printf(3) man page:
    Code:
    CONFORMING TO
           The  fprintf(),  printf(),  sprintf(),  vprintf(), vfprintf(), and vsprintf() functions conform to C89 and C99.
           The snprintf() and vsnprintf() functions conform to C99.
    
           Concerning the return value of snprintf(), SUSv2 and C99 contradict each other: when snprintf() is called  with
           size=0  then  SUSv2 stipulates an unspecified return value less than 1, while C99 allows str to be NULL in this
           case, and gives the return value (as always) as the number of characters that would have been written  in  case
           the output string has been large enough.
    
           Linux  libc4  knows about the five C standard flags.  It knows about the length modifiers h, l, L, and the con-
           versions c, d, e, E, f, F, g, G, i, n, o, p, s, u, x, and X, where F is a  synonym  for  f.   Additionally,  it
           accepts  D, O, and U as synonyms for ld, lo, and lu.  (This is bad, and caused serious bugs later, when support
           for %D disappeared.)  No locale-dependent radix character, no thousands’ separator,  no  NaN  or  infinity,  no
           "%m$" and "*m$".
    
           Linux  libc5 knows about the five C standard flags and the ' flag, locale, "%m$" and "*m$".  It knows about the
           length modifiers h, l, L, Z, and q, but accepts L and q both for long double and for long long int (this  is  a
           bug).   It  no  longer  recognizes  F,  D,  O,  and  U, but adds the conversion character m, which outputs str-
           error(errno).
    
           glibc 2.0 adds conversion characters C and S.
    
           glibc 2.1 adds length modifiers hh, j, t, and z and conversion characters a and A.
    
           glibc 2.2 adds the conversion character F with C99 semantics, and the flag character I.
    So, your format "%35.35e" will need an 'L' modifier to handle the long double type, otherwise you are likely to get a segfault.
    Sometimes, real fast is almost as good as real time.
    Just remember, Semper Gumbi - always be flexible!

  8. #8
    Just Joined!
    Join Date
    Jul 2010
    Posts
    25
    Right. I meant to put the "L" in there. The main thing I wanted to know is if 35 is the right precision - which you seem to confirm - or better yet, if there's a generic way to say "all precision".

  9. #9
    Linux Guru Rubberman's Avatar
    Join Date
    Apr 2009
    Location
    I can be found either 40 miles west of Chicago, in Chicago, or in a galaxy far, far away.
    Posts
    11,380
    This is where you REALLY need to read the manpage in detail yourself: man 3 printf
    But, here are the relevant sections yourself - and experimenting with a test program is a good idea.
    Code:
       The flag characters
           The character % is followed by zero or more of the following flags:
    
           #      The value should be converted to an "alternate form".  For o conversions, the  first  character  of  the
                  output  string  is  made zero (by prefixing a 0 if it was not zero already).  For x and X conversions, a
                  non-zero result has the string "0x" (or "0X" for X conversions) prepended to it.  For a, A, e, E, f,  F,
                  g,  and G conversions, the result will always contain a decimal point, even if no digits follow it (nor-
                  mally, a decimal point appears in the results of those conversions only if a digit follows).  For g  and
                  G  conversions,  trailing  zeros  are not removed from the result as they would otherwise be.  For other
                  conversions, the result is undefined.
    
           0      The value should be zero padded.  For d, i, o, u, x, X, a, A, e, E, f, F, g, and G conversions, the con-
                  verted value is padded on the left with zeros rather than blanks.  If the 0 and - flags both appear, the
                  0 flag is ignored.  If a precision is given with a numeric conversion (d, i, o, u, x, and X), the 0 flag
                  is ignored.  For other conversions, the behavior is undefined.
    
           -      The converted value is to be left adjusted on the field boundary.  (The default is right justification.)
                  Except for n conversions, the converted value is padded on the right with blanks,  rather  than  on  the
                  left with blanks or zeros.  A - overrides a 0 if both are given.
    
           ' '    (a space) A blank should be left before a positive number (or empty string) produced by a signed conver-
                  sion.
    
           +      A sign (+ or -) should always be placed before a number produced by a signed conversion.  By  default  a
                  sign is used only for negative numbers.  A + overrides a space if both are used.
    
           The  five flag characters above are defined in the C standard.  The SUSv2 specifies one further flag character.
    
           '      For decimal conversion (i, d, u, f, F, g, G) the output is to be grouped with thousandsí grouping  char-
                  acters  if  the  locale  information indicates any.  Note that many versions of gcc(1) cannot parse this
                  option and will issue a warning.  SUSv2 does not include %'F.
    
           glibc 2.2 adds one further flag character.
    
           I      For decimal integer conversion (i, d, u) the output uses the localeís alternative output digits, if any.
                  For example, since glibc 2.2.3 this will give Arabic-Indic digits in the Persian ("fa_IR") locale.
    
       The field width
           An  optional  decimal  digit  string (with non-zero first digit) specifying a minimum field width.  If the con-
           verted value has fewer characters than the field width, it will be padded with spaces on the left (or right, if
           the  left-adjustment  flag  has been given).  Instead of a decimal digit string one may write "*" or "*m$" (for
           some decimal integer m) to specify that the field width is given in the next argument, or in the m-th argument,
           respectively,  which must be of type int.  A negative field width is taken as a '-' flag followed by a positive
           field width.  In no case does a nonexistent or small field width cause truncation of a field; if the result  of
           a conversion is wider than the field width, the field is expanded to contain the conversion result.
    
       The precision
           An optional precision, in the form of a period ('.')  followed by an optional decimal digit string.  Instead of
           a decimal digit string one may write "*" or "*m$" (for some decimal integer m) to specify that the precision is
           given in the next argument, or in the m-th argument, respectively, which must be of type int.  If the precision
           is given as just '.', or the precision is negative, the precision is taken to be zero.  This gives the  minimum
           number of digits to appear for d, i, o, u, x, and X conversions, the number of digits to appear after the radix
           character for a, A, e, E, f, and F conversions, the maximum number of significant digits for g  and  G  conver-
           sions, or the maximum number of characters to be printed from a string for s and S conversions.
    
       The length modifier
           Here, "integer conversion" stands for d, i, o, u, x, or X conversion.
    
           hh     A  following integer conversion corresponds to a signed char or unsigned char argument, or a following n
                  conversion corresponds to a pointer to a signed char argument.
    
           h      A following integer conversion corresponds to a short int or unsigned short int argument, or a following
                  n conversion corresponds to a pointer to a short int argument.
    
           l      (ell)  A following integer conversion corresponds to a long int or unsigned long int argument, or a fol-
                  lowing n conversion corresponds to a pointer to a long int argument, or a following c conversion  corre-
                  sponds to a wint_t argument, or a following s conversion corresponds to a pointer to wchar_t argument.
    
           ll     (ell-ell).   A  following  integer  conversion  corresponds to a long long int or unsigned long long int
                  argument, or a following n conversion corresponds to a pointer to a long long int argument.
    
           L      A following a, A, e, E, f, F, g, or G conversion corresponds to a long  double  argument.   (C99  allows
                  %LF, but SUSv2 does not.)
    
           q      ("quad". 4.4BSD and Linux libc5 only.  Donít use.)  This is a synonym for ll.
    
           j      A following integer conversion corresponds to an intmax_t or uintmax_t argument.
    
           z      A  following  integer  conversion  corresponds to a size_t or ssize_t argument.  (Linux libc5 has Z with
                  this meaning.  Donít use it.)
    
           t      A following integer conversion corresponds to a ptrdiff_t argument.
    
           The SUSv2 only knows about the length modifiers h (in hd, hi, ho, hx, hX, hn) and l (in ld, li, lo, lx, lX, ln,
           lc, ls) and L (in Le, LE, Lf, Lg, LG).
    This is from the RH6 (Scientific Linux 6) man page.
    Sometimes, real fast is almost as good as real time.
    Just remember, Semper Gumbi - always be flexible!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •