www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Convert double to long if lossless

reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
I want to convert a double to a long if conversion is lossless 
(without fractional part, non-nan, non-inf, within long-range, 
etc).

I currently have

void foo()
{
     const double value = 10.1;
     try
     {
         import std.conv : to;
         const lvalue = value.to!long;
         if (lvalue == value)
         {
             writeln("lvalue:", lvalue);
             return;
         }
     }
     catch (Exception e) {}
     writeln("value:", value);
}

Can this be improved?

For instance how do I check if a float/double/real has a 
fractional part and avoid entering try-catch for those cases?
Jan 19 2021
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/19/21 6:42 AM, Per Nordlöw wrote:
 I want to convert a double to a long if conversion is lossless (without 
 fractional part, non-nan, non-inf, within long-range, etc).
 
 I currently have
 
 void foo()
 {
      const double value = 10.1;
      try
      {
          import std.conv : to;
          const lvalue = value.to!long;
          if (lvalue == value)
          {
              writeln("lvalue:", lvalue);
              return;
          }
      }
      catch (Exception e) {}
      writeln("value:", value);
 }
 
 Can this be improved?
 
 For instance how do I check if a float/double/real has a fractional part 
 and avoid entering try-catch for those cases?
Use a cast instead. const lvalue = cast(long)value; -Steve
Jan 19 2021
parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Tuesday, 19 January 2021 at 13:36:58 UTC, Steven Schveighoffer 
wrote:
 Use a cast instead.

 const lvalue = cast(long)value;
Ahh, good point. Followd by a compare of the original value I presume.
Jan 19 2021
parent reply drug <drug2004 bk.ru> writes:
On 1/19/21 4:48 PM, Per Nordlöw wrote:
 On Tuesday, 19 January 2021 at 13:36:58 UTC, Steven Schveighoffer wrote:
 Use a cast instead.

 const lvalue = cast(long)value;
Ahh, good point. Followd by a compare of the original value I presume.
don't forget to check by std.math.isFinite before casting Another (low level) way is to shift mantissa left by exponent value. If the remainder equals to zero then no fractal part was encoded in the floating value.
Jan 19 2021
next sibling parent drug <drug2004 bk.ru> writes:
On 1/19/21 5:04 PM, drug wrote:
 On 1/19/21 4:48 PM, Per Nordlöw wrote:
 On Tuesday, 19 January 2021 at 13:36:58 UTC, Steven Schveighoffer wrote:
 Use a cast instead.

 const lvalue = cast(long)value;
Ahh, good point. Followd by a compare of the original value I presume.
don't forget to check by std.math.isFinite before casting Another (low level) way is to shift mantissa left by exponent value. If the remainder equals to zero then no fractal part was encoded in the floating value.
*fractional part
Jan 19 2021
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 1/19/21 6:04 AM, drug wrote:

 Another (low level) way is to shift mantissa left by exponent value.
Luckily, we already have a helper in Phobos: https://dlang.org/phobos/std_bitmanip.html#FloatRep Ali
Jan 19 2021
parent reply drug <drug2004 bk.ru> writes:
On 1/19/21 6:50 PM, Ali Çehreli wrote:
 On 1/19/21 6:04 AM, drug wrote:
 
  > Another (low level) way is to shift mantissa left by exponent value.
 
 Luckily, we already have a helper in Phobos:
 
    https://dlang.org/phobos/std_bitmanip.html#FloatRep
 
 Ali
 
That makes life simpler, thanks for sharing this: ```D import std; void main() { auto valueRange = [ 10.000000000000001, 10.0000000000000001, // in fact this is just 10.0 ]; foreach(value; valueRange) { auto dr = DoubleRep(value); const hasFractional = !!(dr.fraction << (dr.exponent-1023+12)); writefln("has `%.18f` fractional part: %s", value, hasFractional); } } ``` but I think that casting to long and comparing it to the value is easier to understand and more portable: ```D import std; void main() { auto valueRange = [ 10.000000000000001, 10.0000000000000001, // in fact this is 10.0 ]; foreach(value; valueRange) { const hasFractional = (value != cast(long)value); writefln("has `%.18f` fractional part: %s", value, hasFractional); } } ``` P.S. shouldn't compiler emit the error if a literal can't be represented lossless?
Jan 19 2021
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 1/19/21 8:14 AM, drug wrote:

 P.S. shouldn't compiler emit the error if a literal can't be represented
 lossless?
I think it would be a useful improvement. Ali
Jan 19 2021
parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Tuesday, 19 January 2021 at 17:03:53 UTC, Ali Çehreli wrote:
 I think it would be a useful improvement.
Indeed. Maybe Ilya could help out adding this to dmd.
Jan 19 2021
prev sibling parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Tuesday, 19 January 2021 at 16:14:17 UTC, drug wrote:
    https://dlang.org/phobos/std_bitmanip.html#FloatRep
Doesn't this pattern already cover all possible cases of `value` needed? void f(double value) { auto lvalue = cast(long)value; if (lvalue == value) // `value` lacks fraction and in range [long.min .. long.max] { // use long lvalue return; } // use double value }
Jan 19 2021
next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 1/19/21 10:28 AM, Per Nordl=C3=B6w wrote:

 On Tuesday, 19 January 2021 at 16:14:17 UTC, drug wrote:
    https://dlang.org/phobos/std_bitmanip.html#FloatRep
Doesn't this pattern already cover all possible cases of `value` neede=
d? I think so. I just remembered FloatRep as a cleaner tool compared to=20 shifting bits explicitly. Ali
Jan 19 2021
prev sibling parent drug <drug2004 bk.ru> writes:
On 1/19/21 9:28 PM, Per Nordlöw wrote:
 On Tuesday, 19 January 2021 at 16:14:17 UTC, drug wrote:
    https://dlang.org/phobos/std_bitmanip.html#FloatRep
Doesn't this pattern already cover all possible cases of `value` needed? void f(double value) {     auto lvalue = cast(long)value;     if (lvalue == value) // `value` lacks fraction and in range [long.min .. long.max]     {         // use long lvalue         return;     }     // use double value }
Sure. You don't even need to use isFinite like I wrote above because special values like inf and nan become some long values and will never equal to itself. Your question motivated me to do a little workout for my brains. Sorry if it confused you.
Jan 20 2021
prev sibling parent reply kdevel <kdevel vogtner.de> writes:
On Tuesday, 19 January 2021 at 11:42:17 UTC, Per Nordlöw wrote:
 I want to convert a double to a long if conversion is lossless 
 (without fractional part, non-nan, non-inf, within long-range, 
 etc).
And then? I mean: What do you want to do with the long what could not be done with the double in the first place? BTW: Has anybody noticed that there already might have been a loss of precision even before the conversion to long, since the mantissa of the double only entails 53 bit? long q = 1L << 53; long r = q + 1; assert (q != r); double s = q, t = r; assert (s == q); assert (t == r); assert (s != t); // fail
Jan 19 2021
parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Tuesday, 19 January 2021 at 21:04:51 UTC, kdevel wrote:
 And then? I mean: What do you want to do with the long what 
 could not
 be done with the double in the first place?
Motivated question. We want to propagate to our system locally that a double received from a network communication actually is an integer and should be stored and treated as such in an algebraic type.
Jan 19 2021