Thread: How to do a particular conversion?

  1. #16
    Registered User
    Join Date
    Feb 2019
    Posts
    471
    Nope chistop... A little test, shall we:

    Code:
    #include <stdint.h>
    
    int64_t f( double x ) { return x; }
    
    int g( double x, int64_t y ) { return (x - y) * 100; }
    With this you'll get:

    Code:
    f:
      cvttsd2si rax, xmm0
      ret
    
    g:
      pxor  xmm1, xmm1
      cvtsi2sdq xmm1, rdi
      subsd xmm0, xmm1
      mulsd xmm0, QWORD PTR .LC0[rip]  ; .LC0 hold the encoded double 100.0
      cvttsd2si eax, xmm0
      ret
    The first one, CVTTSDSI is a convertion WITH truncation, the second one (on g()) just gets the integral part again, but the calculation is made with rounding on... What I did is to strip the integer part of the double value... 0.3, indeed isn't representable in floating point, nor 0.1, 0.2, 0.4, 0.6, 0.7, 0.8 or 0.9. Floating point is a representation of a FRACTION in the format:

    How to do a particular conversion?-png-latex-png

    Where s, f and e are integers (encoded in floating point structure) and P is the precision of the type, in bits (53 for double). There is no f and e which will give you exactly 0.3.

  2. #17
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    645
    Well yes, of course, binary floating point cannot accurately represent 0.3 (just like decimal cannot accurately represent 1/3, 0.3333...).

    My point was that if the double-precision approximation of 0.3 (0.29999999999999998889776975374843459576368331909 1796875) is converted to integral and fractional components with proper rounding, then you should get 9 and 30 (out of 100). This is the same problem you would face if you were given a value of, say, 0.4999999.... Properly rounded to two digits after the decimal point, it would be 0.50. Truncating gives you 0.49.

  3. #18
    Registered User
    Join Date
    Feb 2019
    Posts
    471
    Quote Originally Posted by christop View Post
    Well yes, of course, binary floating point cannot accurately represent 0.3 (just like decimal cannot accurately represent 1/3, 0.3333...).

    My point was that if the double-precision approximation of 0.3 (0.29999999999999998889776975374843459576368331909 1796875) is converted to integral and fractional components with proper rounding, then you should get 9 and 30 (out of 100). This is the same problem you would face if you were given a value of, say, 0.4999999.... Properly rounded to two digits after the decimal point, it would be 0.50. Truncating gives you 0.49.
    Ok... assuming all values are positive there is an old trick: If you want 2 decimal places as "precision" you could do:

    Code:
    f = (( t - i ) + 0.005)*100;
    Here t is double, f is int and i is int (or int64_t, in my case)... But there is no "rouding" of a floating point value without this trick because there is no values representable between 0.299999999999999988897769753748434595763683319091 796875 and 0.300000000000000044408920985006261616945266723632 8125 in 'double' (you see? I can use 'bc' as well! hehe)...

  4. #19
    misoturbutc Hodor's Avatar
    Join Date
    Nov 2013
    Posts
    1,355
    Quote Originally Posted by flp1969 View Post
    Here t is double, f is int and i is int (or int64_t, in my case)... But there is no "rouding" of a floating point value without this trick because there is no values representable between 0.299999999999999988897769753748434595763683319091 796875 and 0.300000000000000044408920985006261616945266723632 8125 in 'double' (you see? I can use 'bc' as well! hehe)...
    I don't really understand the issue here

    Code:
    #include <stdio.h>
    #include <math.h>
    
    int main(void)
    {
        const double testvalues[] = {
            3.14, 9.3, 9.28, 9.29, 1.1, 0.1, 0.9, 1.0/5, 1.0/3,
            0.299999999999999988897769753748434595763683319091796875
        };
        double t;
        int i, f;
        unsigned it;
        
        for (it = 0; it < sizeof testvalues / sizeof testvalues[0]; ++it) {
            t = testvalues[it];
            i = t;
            f = round((t - i) * 100.0);
            //f = ((t - i) + 0.005) * 100;
            printf("%f: %d, %d\n", t, i, f);
        }
        
        return 0;
    }
    gives

    Code:
    3.140000: 3, 14
    9.300000: 9, 30
    9.280000: 9, 28
    9.290000: 9, 29
    1.100000: 1, 10
    0.100000: 0, 10
    0.900000: 0, 90
    0.200000: 0, 20
    0.333333: 0, 33
    0.300000: 0, 30
    Which is what is expected (?)

  5. #20
    Registered User
    Join Date
    Feb 2019
    Posts
    471
    Quote Originally Posted by Hodor View Post
    I don't really understand the issue here
    Notice I didn't call a libm function. No extra libraries needed to be linked... No round() call... which is a little bit faster.

  6. #21
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    645
    Quote Originally Posted by Hodor View Post
    I don't really understand the issue here

    Code:
    #include <stdio.h>
    #include <math.h>
    
    int main(void)
    {
        const double testvalues[] = {
            3.14, 9.3, 9.28, 9.29, 1.1, 0.1, 0.9, 1.0/5, 1.0/3,
            0.299999999999999988897769753748434595763683319091796875
        };
        double t;
        int i, f;
        unsigned it;
        
        for (it = 0; it < sizeof testvalues / sizeof testvalues[0]; ++it) {
            t = testvalues[it];
            i = t;
            f = round((t - i) * 100.0);
            //f = ((t - i) + 0.005) * 100;
            printf("%f: %d, %d\n", t, i, f);
        }
        
        return 0;
    }
    gives

    Code:
    3.140000: 3, 14
    9.300000: 9, 30
    9.280000: 9, 28
    9.290000: 9, 29
    1.100000: 1, 10
    0.100000: 0, 10
    0.900000: 0, 90
    0.200000: 0, 20
    0.333333: 0, 33
    0.300000: 0, 30
    Which is what is expected (?)
    Try adding the value 0.999 to your test cases:

    Code:
    0.999000: 0, 100
    Oops!

    You need to round t (either with the round() function or by adding 0.005) before converting it to a integral and fractional parts.

    Even then there may be edge cases where conversion gives a wrong result. Floating point can be tricky! But it should be easy enough to iterate over all possible values of a double between about 0.005 and 9223372036854775000 (the maximum value of a double-precision number that fits in a signed 64-bit integer) to make sure they're all converted properly. There's only about 324259173170675712 values to check, so you could check them all in about 10 years, assuming you can check one billion values per second.

  7. #22
    Registered User
    Join Date
    Feb 2019
    Posts
    471
    Quote Originally Posted by christop View Post
    there's only about 324259173170675712 values to check, so you could check them all in about 10 years, assuming you can check one billion values per second.
    bwahahahahahahahahahaha!!!!

  8. #23
    misoturbutc Hodor's Avatar
    Join Date
    Nov 2013
    Posts
    1,355
    Quote Originally Posted by christop View Post
    Try adding the value 0.999 to your test cases:

    Code:
    0.999000: 0, 100
    Oops!

    You need to round t (either with the round() function or by adding 0.005) before converting it to a integral and fractional parts.

    Even then there may be edge cases where conversion gives a wrong result. Floating point can be tricky! But it should be easy enough to iterate over all possible values of a double between about 0.005 and 9223372036854775000 (the maximum value of a double-precision number that fits in a signed 64-bit integer) to make sure they're all converted properly. There's only about 324259173170675712 values to check, so you could check them all in about 10 years, assuming you can check one billion values per second.
    For 0.999 in this scenario I would expect the answer to be 0, 100 so that's not an oops. Even if you disagree, the point of my quick and dirty "test" program was not to handle every single edge case but to demonstrate that what was being discussed (i.e. that working with 0.3 could never work) didn't make sense.

  9. #24
    misoturbutc Hodor's Avatar
    Join Date
    Nov 2013
    Posts
    1,355
    Quote Originally Posted by flp1969 View Post
    Notice I didn't call a libm function. No extra libraries needed to be linked... No round() call... which is a little bit faster.
    Well, the discussion wasn't about using or not using library calls so I'm not sure what you point is. Plus I don't think your method is going to work very well for amounts such as -0.33 because it'll "round" in the wrong direction

  10. #25
    Registered User
    Join Date
    Feb 2019
    Posts
    471
    Quote Originally Posted by Hodor View Post
    Well, the discussion wasn't about using or not using library calls so I'm not sure what you point is. Plus I don't think your method is going to work very well for amounts such as -0.33 because it'll "round" in the wrong direction
    Ok... forget everything about it then... good luck!

  11. #26
    misoturbutc Hodor's Avatar
    Join Date
    Nov 2013
    Posts
    1,355
    Quote Originally Posted by flp1969 View Post
    Ok... forget everything about it then... good luck!
    Why would I want to forget about it? It's interesting if only because there are people out there insane enough to use floating point numbers for dealing with money

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. bcc conversion
    By sjhawar in forum C Programming
    Replies: 1
    Last Post: 06-12-2011, 01:50 PM
  2. C# to C++ conversion?
    By hammari in forum C# Programming
    Replies: 8
    Last Post: 03-18-2011, 12:51 AM
  3. C to C++ conversion
    By racerday182 in forum C++ Programming
    Replies: 5
    Last Post: 12-03-2008, 06:20 PM
  4. Conversion
    By justgotthis in forum C Programming
    Replies: 4
    Last Post: 10-05-2005, 06:37 AM
  5. Conversion from C++ to C
    By MasterGBC in forum C Programming
    Replies: 4
    Last Post: 04-04-2003, 01:19 AM

Tags for this Thread