www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Floating point in the type system

reply "Robert" <robert octarineparrot.com> writes:
Hi all,

I came across this example, and wondered what your thoughts on it 
are:


```
void main(string[] args)
{
     struct Foo(float f) {
         alias VAL = f;
         float getF() {
             return f;
         }
     }

     Foo!(float.nan) f;
     Foo!(float.nan) f2;

     // This will fail at compile time
     static assert(f.VAL == f2.VAL);

     // This will fail at run time
     assert(f.getF() == f2.getF());

     // But this is ok
     f = f2;
}
```

It seems a little unusual to me.

Robert
Sep 12 2015
next sibling parent reply "Atila Neves" <atila.neves gmail.com> writes:
On Saturday, 12 September 2015 at 15:13:27 UTC, Robert wrote:
 Hi all,

 I came across this example, and wondered what your thoughts on 
 it are:


 ```
 void main(string[] args)
 {
     struct Foo(float f) {
         alias VAL = f;
         float getF() {
             return f;
         }
     }

     Foo!(float.nan) f;
     Foo!(float.nan) f2;

     // This will fail at compile time
     static assert(f.VAL == f2.VAL);

     // This will fail at run time
     assert(f.getF() == f2.getF());

     // But this is ok
     f = f2;
 }
 ```

 It seems a little unusual to me.

 Robert
What do think is unusual? Atila
Sep 12 2015
parent reply "Robert" <robert octarineparrot.com> writes:
On Saturday, 12 September 2015 at 15:49:23 UTC, Atila Neves wrote:
 On Saturday, 12 September 2015 at 15:13:27 UTC, Robert wrote:
 Hi all,

 I came across this example, and wondered what your thoughts on 
 it are:


 ```
 void main(string[] args)
 {
     struct Foo(float f) {
         alias VAL = f;
         float getF() {
             return f;
         }
     }

     Foo!(float.nan) f;
     Foo!(float.nan) f2;

     // This will fail at compile time
     static assert(f.VAL == f2.VAL);

     // This will fail at run time
     assert(f.getF() == f2.getF());

     // But this is ok
     f = f2;
 }
 ```

 It seems a little unusual to me.

 Robert
What do think is unusual? Atila
It's unusual, because `float.nan != float.nan`, so one might expect that `typeof(Foo!(float.nan) != Foo!(float.nan))`, whereas this is clearly not the case, even with both the static assert and runtime assert failing. I'm just curious to understand the reasoning behind this, whether it's intentional, and whether it matters at all.
Sep 12 2015
next sibling parent "Xinok" <xinok live.com> writes:
On Saturday, 12 September 2015 at 16:08:31 UTC, Robert wrote:
 On Saturday, 12 September 2015 at 15:49:23 UTC, Atila Neves 
 wrote:
 What do think is unusual?

 Atila
It's unusual, because `float.nan != float.nan`, so one might expect that `typeof(Foo!(float.nan) != Foo!(float.nan))`, whereas this is clearly not the case, even with both the static assert and runtime assert failing. I'm just curious to understand the reasoning behind this, whether it's intentional, and whether it matters at all.
(1) f = f2; // This is assignment, not comparison (2) alias VAL = f; // This is not a data member so is not involved in comparisons or assignments change "alias VAL" to "float VAL" and then you might see the behavior you expect.
Sep 12 2015
prev sibling next sibling parent anonymous <anonymous example.com> writes:
On Saturday 12 September 2015 18:08, Robert wrote:

 It's unusual, because `float.nan != float.nan`, so one might 
 expect that `typeof(Foo!(float.nan) != Foo!(float.nan))`, whereas 
 this is clearly not the case, even with both the static assert 
 and runtime assert failing. I'm just curious to understand the 
 reasoning behind this, whether it's intentional, and whether it 
 matters at all.
I don't know what the compiler actually does, but it looks like the comparison of template value arguments doesn't use equality, but something more akin to `is` instead (bitwise equality). If that's right, then `is(Foo!(float.nan) == Foo!(float.nan))` holds because `float.nan is float.nan` holds. Same behavior with a struct instead of float: ---- struct S { bool opEquals(S other) {return false;} } struct Foo(S s) { } static assert(S.init != S.init); /* not equal */ static assert(S.init is S.init); /* but bit for bit identical */ static assert(is(Foo!(S.init) == Foo!(S.init))); ----
Sep 12 2015
prev sibling parent "Ola Fosheim Grostad" <ola.fosheim.grostad+dlang gmail.com> writes:
On Saturday, 12 September 2015 at 16:08:31 UTC, Robert wrote:
 assert and runtime assert failing. I'm just curious to 
 understand the reasoning behind this, whether it's intentional, 
 and whether it matters at all.
Types need to mangle to the same name, but using floats in a type name is usually not a good idea. Try pi... How many decimals? A roundoff error and you get a new type.
Sep 12 2015
prev sibling parent reply "Robert" <robert octarineparrot.com> writes:
On Saturday, 12 September 2015 at 15:13:27 UTC, Robert wrote:
 Hi all,

 I came across this example, and wondered what your thoughts on 
 it are:


 <snip>

 It seems a little unusual to me.

 Robert
For what it's worth, I was investigating this initially as a discussion about adding type-level values in Rust, and how to handle unusual cases like this. In the process we've managed to break the Idris type system: https://github.com/idris-lang/Idris-dev/issues/2609. There's been quite a lot of interesting discussion about it on the IRC channels for all three languages :) I'd be interested to know how other languages handle this, if anyone knows.
Sep 12 2015
next sibling parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Saturday, 12 September 2015 at 19:02:16 UTC, Robert wrote:
 For what it's worth, I was investigating this initially as a 
 discussion about adding type-level values in Rust, and how to 
 handle unusual cases like this.
C++ does not allow it. And frankly, having more than a single integer value type as a value parameter in C++ templates is a pointless PITA. I think Go got literal constant values right: make them untyped until bound.
Sep 12 2015
prev sibling parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Saturday, 12 September 2015 at 19:02:16 UTC, Robert wrote:
 handle unusual cases like this. In the process we've managed to 
 break the Idris type system: 
 https://github.com/idris-lang/Idris-dev/issues/2609.
I don't know Idris, but you can't easily unify over floats, because they are samples on an interval, and don't behave like real numbers, but more like underspecified intervals from interval arithmetics. If you want to unify over reals, you probably should do it symbolically. Or just treat float values as literal symbols in the type system. It is useful for configuration: Frequency!342.234 Sometimes it is worthwhile to have an unsound type system. Both D and Dart have somewhat unsound type systems. It puts a burden on the programmer, but can be useful.
Sep 12 2015