Commit 8bf4b3c611
Changed files (5)
src/clang.zig
@@ -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;
src/translate_c.zig
@@ -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);
src/zig_clang.cpp
@@ -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());
src/zig_clang.h
@@ -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);
test/translate_c.zig
@@ -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",