summaryrefslogtreecommitdiff
path: root/chapter22
diff options
context:
space:
mode:
Diffstat (limited to 'chapter22')
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/AddFNode.java3
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/DivFNode.java3
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/MulFNode.java3
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/SubFNode.java3
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/FloatPrecisionTest.java61
5 files changed, 73 insertions, 0 deletions
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/AddFNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/AddFNode.java
index fd713156..3b37dc84 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/node/AddFNode.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/AddFNode.java
@@ -14,10 +14,13 @@ public class AddFNode extends Node {
@Override
public Type compute() {
+ if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return Type.TOP;
if (in(1)._type instanceof TypeFloat i0 &&
in(2)._type instanceof TypeFloat i1) {
if (i0.isConstant() && i1.isConstant())
return TypeFloat.constant(i0.value()+i1.value());
+ // Two f32 inputs can still produce a result needing f64 precision.
+ return TypeFloat.F64;
}
return in(1)._type.meet(in(2)._type);
}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/DivFNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/DivFNode.java
index 149499f9..a0d95e06 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/node/DivFNode.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/DivFNode.java
@@ -14,10 +14,13 @@ public class DivFNode extends Node {
@Override
public Type compute() {
+ if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return Type.TOP;
if (in(1)._type instanceof TypeFloat i0 &&
in(2)._type instanceof TypeFloat i1) {
if (i0.isConstant() && i1.isConstant())
return TypeFloat.constant(i0.value()/i1.value());
+ // Two f32 inputs can still produce a result needing f64 precision.
+ return TypeFloat.F64;
}
return in(1)._type.meet(in(2)._type);
}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/MulFNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/MulFNode.java
index 6a57f3c5..f24270cf 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/node/MulFNode.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/MulFNode.java
@@ -14,10 +14,13 @@ public class MulFNode extends Node {
@Override
public Type compute() {
+ if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return Type.TOP;
if (in(1)._type instanceof TypeFloat i0 &&
in(2)._type instanceof TypeFloat i1) {
if (i0.isConstant() && i1.isConstant())
return TypeFloat.constant(i0.value()*i1.value());
+ // Two f32 inputs can still produce a result needing f64 precision.
+ return TypeFloat.F64;
}
return in(1)._type.meet(in(2)._type);
}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/SubFNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/SubFNode.java
index 2d218a03..4dbc141e 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/node/SubFNode.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/SubFNode.java
@@ -14,10 +14,13 @@ public class SubFNode extends Node {
@Override
public Type compute() {
+ if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return Type.TOP;
if (in(1)._type instanceof TypeFloat i0 &&
in(2)._type instanceof TypeFloat i1) {
if (i0.isConstant() && i1.isConstant())
return TypeFloat.constant(i0.value()-i1.value());
+ // Two f32 inputs can still produce a result needing f64 precision.
+ return TypeFloat.F64;
}
return in(1)._type.meet(in(2)._type);
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/FloatPrecisionTest.java b/chapter22/src/test/java/com/seaofnodes/simple/FloatPrecisionTest.java
new file mode 100644
index 00000000..b753ea79
--- /dev/null
+++ b/chapter22/src/test/java/com/seaofnodes/simple/FloatPrecisionTest.java
@@ -0,0 +1,61 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class FloatPrecisionTest {
+ @Test public void testFloatPrecision() {
+ String[] ops = {"+1.0","-1.0","*3.0","/3.0"};
+ long[] args = {16777216,-16777216,16777215,1};
+ double[] results = {16777217.0,-16777217.0,50331645.0,1.0/3.0};
+ for( int i = 0; i<ops.length; i++ ) {
+ String expr = "x"+ops[i];
+ assertEquals(expr,Double.toString(results[i]),
+ evaluate("f32 x = arg; return "+expr+";",args[i]));
+ assertEquals(expr,Double.toString((float)results[i]),
+ evaluate("f32 x = arg; f32 y = "+expr+"; return y;",args[i]));
+ }
+ }
+
+ private static String evaluate(String src, long arg) {
+ CodeGen code = new CodeGen(src).parse().iter().finalizeIR();
+ return Eval2.eval(code,arg);
+ }
+
+ @Test public void testMonotoneCompute() {
+ new CodeGen("");
+ ArrayList<Type> inputs = new ArrayList<>(Arrays.asList(
+ Type.TOP,Type.BOTTOM,TypeFloat.F32,TypeFloat.F64,
+ TypeFloat.F32.dual(),TypeFloat.F64.dual()));
+ for( double value : new double[]{-0.0,0.0,-1.0,1.0,3.0,16777216.0,16777217.0,
+ Double.NaN,Double.NEGATIVE_INFINITY,Double.POSITIVE_INFINITY} )
+ inputs.add(TypeFloat.constant(value));
+ // Isolated analysis inputs; no peepholes can fold the tested nodes.
+ Node lhs = new ConstantNode(Type.BOTTOM);
+ Node rhs = new ConstantNode(Type.BOTTOM);
+ for( Node op : new Node[]{new AddFNode(lhs,rhs),new SubFNode(lhs,rhs),
+ new MulFNode(lhs,rhs),new DivFNode(lhs,rhs)} ) {
+ Type[][] results = new Type[inputs.size()][inputs.size()];
+ for( int i = 0; i<inputs.size(); i++ )
+ for( int j = 0; j<inputs.size(); j++ ) {
+ lhs._type = inputs.get(i);
+ rhs._type = inputs.get(j);
+ results[i][j] = op.compute();
+ }
+ for( int i = 0; i<inputs.size(); i++ )
+ for( int j = 0; j<inputs.size(); j++ )
+ if( inputs.get(i).isa(inputs.get(j)) )
+ for( int k = 0; k<inputs.size(); k++ )
+ if( !results[i][k].isa(results[j][k]) || !results[k][i].isa(results[k][j]) )
+ fail(op.label()+": "+inputs.get(i)+" -> "+inputs.get(j)+
+ ", other="+inputs.get(k)+"; lhs results "+results[i][k]+" -> "+results[j][k]+
+ "; rhs results "+results[k][i]+" -> "+results[k][j]);
+ }
+ }
+}