translate-c: translate 80/128-bit long double literals

This commit is contained in:
Carl Åstholm 2023-11-01 19:12:54 +01:00 committed by Veikka Tuominen
parent 40bd93e2a2
commit 8bf4b3c611
5 changed files with 77 additions and 15 deletions

View File

@ -506,6 +506,9 @@ pub const FloatingLiteral = opaque {
pub const getValueAsApproximateDouble = ZigClangFloatingLiteral_getValueAsApproximateDouble;
extern fn ZigClangFloatingLiteral_getValueAsApproximateDouble(*const FloatingLiteral) f64;
pub const getValueAsApproximateQuadBits = ZigClangFloatingLiteral_getValueAsApproximateQuadBits;
extern fn ZigClangFloatingLiteral_getValueAsApproximateQuadBits(*const FloatingLiteral, low: *u64, high: *u64) void;
pub const getBeginLoc = ZigClangFloatingLiteral_getBeginLoc;
extern fn ZigClangFloatingLiteral_getBeginLoc(*const FloatingLiteral) SourceLocation;

View File

@ -3936,11 +3936,26 @@ fn transCPtrCast(
}
fn transFloatingLiteral(c: *Context, expr: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
// TODO use something more accurate than widening to a larger float type and printing that result
switch (expr.getRawSemantics()) {
.IEEEhalf, // f16
.IEEEsingle, // f32
.IEEEdouble, // f64
=> {},
=> {
var dbl = expr.getValueAsApproximateDouble();
const is_negative = dbl < 0; // -0.0 is considered non-negative
if (is_negative) dbl = -dbl;
const str = if (dbl == @floor(dbl))
try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl})
else
try std.fmt.allocPrint(c.arena, "{d}", .{dbl});
var node = try Tag.float_literal.create(c.arena, str);
if (is_negative) node = try Tag.negate.create(c.arena, node);
return maybeSuppressResult(c, used, node);
},
.x87DoubleExtended, // f80
.IEEEquad, // f128
=> return transFloatingLiteralQuad(c, expr, used),
else => |format| return fail(
c,
error.UnsupportedTranslation,
@ -3949,14 +3964,44 @@ fn transFloatingLiteral(c: *Context, expr: *const clang.FloatingLiteral, used: R
.{format},
),
}
// TODO use something more accurate
var dbl = expr.getValueAsApproximateDouble();
const is_negative = dbl < 0;
if (is_negative) dbl = -dbl;
const str = if (dbl == @floor(dbl))
try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl})
else
try std.fmt.allocPrint(c.arena, "{d}", .{dbl});
}
fn transFloatingLiteralQuad(c: *Context, expr: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
assert(switch (expr.getRawSemantics()) {
.x87DoubleExtended, .IEEEquad => true,
else => false,
});
var low: u64 = undefined;
var high: u64 = undefined;
expr.getValueAsApproximateQuadBits(&low, &high);
var quad: f128 = @bitCast(low | @as(u128, high) << 64);
const is_negative = quad < 0; // -0.0 is considered non-negative
if (is_negative) quad = -quad;
// TODO implement decimal format for f128 <https://github.com/ziglang/zig/issues/1181>
// in the meantime, if the value can be roundtripped by casting it to f64, serializing it to
// the decimal format and parsing it back as the exact same f128 value, then use that serialized form
const str = fmt_decimal: {
var buf: [512]u8 = undefined; // should be large enough to print any f64 in decimal form
const dbl: f64 = @floatCast(quad);
const temp_str = if (dbl == @floor(dbl))
std.fmt.bufPrint(&buf, "{d}.0", .{dbl}) catch |err| switch (err) {
error.NoSpaceLeft => unreachable,
}
else
std.fmt.bufPrint(&buf, "{d}", .{dbl}) catch |err| switch (err) {
error.NoSpaceLeft => unreachable,
};
const could_roundtrip = if (std.fmt.parseFloat(f128, temp_str)) |parsed_quad|
quad == parsed_quad
else |_|
false;
break :fmt_decimal if (could_roundtrip) try c.arena.dupe(u8, temp_str) else null;
}
// otherwise, fall back to the hexadecimal format
orelse try std.fmt.allocPrint(c.arena, "{x}", .{quad});
var node = try Tag.float_literal.create(c.arena, str);
if (is_negative) node = try Tag.negate.create(c.arena, node);
return maybeSuppressResult(c, used, node);

View File

@ -3245,6 +3245,17 @@ double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatin
return casted->getValueAsApproximateDouble();
}
void ZigClangFloatingLiteral_getValueAsApproximateQuadBits(const ZigClangFloatingLiteral *self, uint64_t *low, uint64_t *high) {
auto casted = reinterpret_cast<const clang::FloatingLiteral *>(self);
llvm::APFloat apf = casted->getValue();
bool ignored;
apf.convert(llvm::APFloat::IEEEquad(), llvm::APFloat::rmNearestTiesToEven, &ignored);
const llvm::APInt api = apf.bitcastToAPInt();
const uint64_t *api_data = api.getRawData();
*low = api_data[0];
*high = api_data[1];
}
struct ZigClangSourceLocation ZigClangFloatingLiteral_getBeginLoc(const struct ZigClangFloatingLiteral *self) {
auto casted = reinterpret_cast<const clang::FloatingLiteral *>(self);
return bitcast(casted->getBeginLoc());

View File

@ -1510,6 +1510,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st
ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST,
unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM);
ZIG_EXTERN_C double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self);
ZIG_EXTERN_C void ZigClangFloatingLiteral_getValueAsApproximateQuadBits(const ZigClangFloatingLiteral *self, uint64_t *low, uint64_t *high);
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFloatingLiteral_getBeginLoc(const struct ZigClangFloatingLiteral *);
ZIG_EXTERN_C ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self);

View File

@ -1240,6 +1240,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\extern const float my_float = 1.0f;
\\extern const double my_double = 1.0;
\\extern const long double my_longdouble = 1.0l;
\\extern const long double my_extended_precision_longdouble = 1.0000000000000003l;
, &([_][]const u8{
"pub const foo = @as(f32, 3.14);",
"pub const bar = @as(c_longdouble, 16.0e-2);",
@ -1250,13 +1251,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
"pub const foobar = -@as(c_longdouble, 73.0);",
"pub export const my_float: f32 = 1.0;",
"pub export const my_double: f64 = 1.0;",
} ++ if (@bitSizeOf(c_longdouble) != 64) .{
// TODO properly translate non-64-bit long doubles
"source.h:10:42: warning: unsupported floating point constant format",
"source.h:10:26: warning: unable to translate variable initializer, demoted to extern",
"pub extern const my_longdouble: c_longdouble;",
} else .{
"pub export const my_longdouble: c_longdouble = 1.0;",
switch (@bitSizeOf(c_longdouble)) {
// TODO implement decimal format for f128 <https://github.com/ziglang/zig/issues/1181>
// (so that f80/f128 values not exactly representable as f64 can be emitted in decimal form)
80 => "pub export const my_extended_precision_longdouble: c_longdouble = 0x1.000000000000159ep0;",
128 => "pub export const my_extended_precision_longdouble: c_longdouble = 0x1.000000000000159e05f1e2674d21p0;",
else => "pub export const my_extended_precision_longdouble: c_longdouble = 1.0000000000000002;",
},
}));
cases.add("macro defines hexadecimal float",