Skip to content

Commit 7ac4fda

Browse files
author
Lukasz A.J. Wrona
committed
Refactor and expose floating point to java conversion in expr2java.h
This change exposes new floating_point_to_java_string function which lets the user convert floating point numbers to java-compatible strings without having to cast them to expression and creating an unnecessary namespace beforehand.
1 parent 1a88479 commit 7ac4fda

File tree

2 files changed

+57
-47
lines changed

2 files changed

+57
-47
lines changed

src/java_bytecode/expr2java.cpp

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -228,53 +228,11 @@ std::string expr2javat::convert_constant(
228228
else if((src.type()==java_float_type()) ||
229229
(src.type()==java_double_type()))
230230
{
231-
ieee_floatt ieee_repr(to_constant_expr(src));
232-
std::string result;
233-
234-
bool is_java_float=(src.type()==java_float_type());
235-
if(ieee_repr.is_zero())
236-
{
237-
if(ieee_repr.get_sign())
238-
result+='-';
239-
result+="0.0";
240-
if(is_java_float)
241-
result+='f';
242-
return result;
243-
}
244-
245-
if(ieee_repr.is_NaN())
246-
{
247-
if(is_java_float)
248-
return "Float.NaN";
249-
else
250-
return "Double.NaN";
251-
}
252-
253-
if(ieee_repr.is_infinity())
254-
{
255-
if(is_java_float)
256-
{
257-
if(ieee_repr.get_sign())
258-
return "Float.NEGATIVE_INFINITY";
259-
else
260-
return "Float.POSITIVE_INFINITY";
261-
}
262-
else
263-
{
264-
if(ieee_repr.get_sign())
265-
return "Double.NEGATIVE_INFINITY";
266-
else
267-
return "Double.POSITIVE_INFINITY";
268-
}
269-
}
270-
271-
std::stringstream buffer;
272-
buffer << std::hexfloat;
273-
if(is_java_float)
274-
buffer << ieee_repr.to_float() << 'f';
275-
else
276-
buffer << ieee_repr.to_double();
277-
return buffer.str();
231+
// This converts NaNs to the canonical NaN
232+
const ieee_floatt ieee_repr(to_constant_expr(src));
233+
if(ieee_repr.is_double())
234+
return floating_point_to_java_string(ieee_repr.to_double());
235+
return floating_point_to_java_string(ieee_repr.to_float());
278236
}
279237

280238

src/java_bytecode/expr2java.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ Author: Daniel Kroening, kroening@cs.cmu.edu
1010
#ifndef CPROVER_JAVA_BYTECODE_EXPR2JAVA_H
1111
#define CPROVER_JAVA_BYTECODE_EXPR2JAVA_H
1212

13+
#include <cmath>
14+
#include <numeric>
15+
#include <sstream>
1316
#include <string>
1417
#include <ansi-c/expr2c_class.h>
1518

@@ -50,4 +53,53 @@ class expr2javat:public expr2ct
5053
std::string expr2java(const exprt &expr, const namespacet &ns);
5154
std::string type2java(const typet &type, const namespacet &ns);
5255

56+
/// Convert floating point number to a string without printing
57+
/// unnecessary zeros. Prints decimal if precision is not lost.
58+
/// Prints hexadecimal otherwise, and/or java-friendly NaN and Infinity
59+
template <typename float_type>
60+
std::string floating_point_to_java_string(float_type value)
61+
{
62+
const auto is_float = std::is_same<float_type, float>::value;
63+
static const std::string class_name = is_float ? "Float" : "Double";
64+
if(std::isnan(value))
65+
return class_name + ".NaN";
66+
if(std::isinf(value) && value >= 0.)
67+
return class_name + ".POSITIVE_INFINITY";
68+
if(std::isinf(value) && value <= 0.)
69+
return class_name + ".NEGATIVE_INFINITY";
70+
const std::string decimal = [&]() -> std::string { // NOLINT
71+
// Using ostringstream instead of to_string to get string without
72+
// trailing zeros
73+
std::ostringstream raw_stream;
74+
raw_stream << value;
75+
const auto raw_decimal = raw_stream.str();
76+
if(raw_decimal.find('.') == std::string::npos)
77+
return raw_decimal + ".0";
78+
return raw_decimal;
79+
}();
80+
const bool is_lossless = [&] { // NOLINT
81+
if(value == std::numeric_limits<float_type>::min())
82+
return true;
83+
try
84+
{
85+
return std::stod(decimal) == value;
86+
}
87+
catch(std::out_of_range)
88+
{
89+
return false;
90+
}
91+
}();
92+
const std::string lossless = [&]() -> std::string { // NOLINT
93+
if(is_lossless)
94+
return decimal;
95+
std::ostringstream stream;
96+
stream << std::hexfloat << value;
97+
return stream.str();
98+
}();
99+
const auto literal = is_float ? lossless + 'f' : lossless;
100+
if(is_lossless)
101+
return literal;
102+
return literal + " /* " + decimal + " */";
103+
}
104+
53105
#endif // CPROVER_JAVA_BYTECODE_EXPR2JAVA_H

0 commit comments

Comments
 (0)