Jump to page: 1 2
Thread overview
Convert double to long if lossless
Jan 19, 2021
Per Nordlöw
Jan 19, 2021
Per Nordlöw
Jan 19, 2021
drug
Jan 19, 2021
drug
Jan 19, 2021
Ali Çehreli
Jan 19, 2021
drug
Jan 19, 2021
Ali Çehreli
Jan 19, 2021
Per Nordlöw
Jan 19, 2021
Per Nordlöw
Jan 19, 2021
Ali Çehreli
Jan 20, 2021
drug
Jan 19, 2021
kdevel
Jan 19, 2021
Per Nordlöw
January 19, 2021
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?
January 19, 2021
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
January 19, 2021
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.
January 19, 2021
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.
January 19, 2021
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
January 19, 2021
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

January 19, 2021
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?
January 19, 2021
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

January 19, 2021
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.
January 19, 2021
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
}

« First   ‹ Prev
1 2