| ... | @@ -225,6 +225,177 @@ fn addXf3(comptime T: type, a: T, b: T) T { | ... | @@ -225,6 +225,177 @@ fn addXf3(comptime T: type, a: T, b: T) T { |
| 225 | return @bitCast(T, result); | 225 | return @bitCast(T, result); |
| 226 | } | 226 | } |
| 227 | | 227 | |
| | 228 | fn normalize_f80(exp: *i32, significand: *u80) void { |
| | 229 | const shift = @clz(u64, @truncate(u64, significand.*)); |
| | 230 | significand.* = (significand.* << shift); |
| | 231 | exp.* += -@as(i8, shift); |
| | 232 | } |
| | 233 | |
| | 234 | pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { |
| | 235 | var a_rep align(16) = @ptrCast(*const std.math.F80Repr, &a).*; |
| | 236 | var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*; |
| | 237 | var a_exp: i32 = a_rep.exp & 0x7FFF; |
| | 238 | var b_exp: i32 = b_rep.exp & 0x7FFF; |
| | 239 | |
| | 240 | const significand_bits = std.math.floatMantissaBits(f80); |
| | 241 | const int_bit = 0x8000000000000000; |
| | 242 | const significand_mask = 0x7FFFFFFFFFFFFFFF; |
| | 243 | const qnan_bit = 0xC000000000000000; |
| | 244 | const max_exp = 0x7FFF; |
| | 245 | const sign_bit = 0x8000; |
| | 246 | |
| | 247 | // Detect if a or b is infinity, or NaN. |
| | 248 | if (a_exp == max_exp) { |
| | 249 | if (a_rep.fraction ^ int_bit == 0) { |
| | 250 | if (b_exp == max_exp and (b_rep.fraction ^ int_bit == 0)) { |
| | 251 | // +/-infinity + -/+infinity = qNaN |
| | 252 | return std.math.qnan_f80; |
| | 253 | } |
| | 254 | // +/-infinity + anything = +/- infinity |
| | 255 | return a; |
| | 256 | } else { |
| | 257 | std.debug.assert(a_rep.fraction & significand_mask != 0); |
| | 258 | // NaN + anything = qNaN |
| | 259 | a_rep.fraction |= qnan_bit; |
| | 260 | return @ptrCast(*const f80, &a_rep).*; |
| | 261 | } |
| | 262 | } |
| | 263 | if (b_exp == max_exp) { |
| | 264 | if (b_rep.fraction ^ int_bit == 0) { |
| | 265 | // anything + +/-infinity = +/-infinity |
| | 266 | return b; |
| | 267 | } else { |
| | 268 | std.debug.assert(b_rep.fraction & significand_mask != 0); |
| | 269 | // anything + NaN = qNaN |
| | 270 | b_rep.fraction |= qnan_bit; |
| | 271 | return @ptrCast(*const f80, &b_rep).*; |
| | 272 | } |
| | 273 | } |
| | 274 | |
| | 275 | const a_zero = (a_rep.fraction | @bitCast(u32, a_exp)) == 0; |
| | 276 | const b_zero = (b_rep.fraction | @bitCast(u32, b_exp)) == 0; |
| | 277 | if (a_zero) { |
| | 278 | // zero + anything = anything |
| | 279 | if (b_zero) { |
| | 280 | // but we need to get the sign right for zero + zero |
| | 281 | a_rep.exp &= b_rep.exp; |
| | 282 | return @ptrCast(*const f80, &a_rep).*; |
| | 283 | } else { |
| | 284 | return b; |
| | 285 | } |
| | 286 | } else if (b_zero) { |
| | 287 | // anything + zero = anything |
| | 288 | return a; |
| | 289 | } |
| | 290 | |
| | 291 | var a_int: u80 = a_rep.fraction | (@as(u80, a_rep.exp & max_exp) << significand_bits); |
| | 292 | var b_int: u80 = b_rep.fraction | (@as(u80, b_rep.exp & max_exp) << significand_bits); |
| | 293 | |
| | 294 | // Swap a and b if necessary so that a has the larger absolute value. |
| | 295 | if (b_int > a_int) { |
| | 296 | const temp = a_rep; |
| | 297 | a_rep = b_rep; |
| | 298 | b_rep = temp; |
| | 299 | } |
| | 300 | |
| | 301 | // Extract the exponent and significand from the (possibly swapped) a and b. |
| | 302 | a_exp = a_rep.exp & max_exp; |
| | 303 | b_exp = b_rep.exp & max_exp; |
| | 304 | a_int = a_rep.fraction; |
| | 305 | b_int = b_rep.fraction; |
| | 306 | |
| | 307 | // Normalize any denormals, and adjust the exponent accordingly. |
| | 308 | normalize_f80(&a_exp, &a_int); |
| | 309 | normalize_f80(&b_exp, &b_int); |
| | 310 | |
| | 311 | // The sign of the result is the sign of the larger operand, a. If they |
| | 312 | // have opposite signs, we are performing a subtraction; otherwise addition. |
| | 313 | const result_sign = a_rep.exp & sign_bit; |
| | 314 | const subtraction = (a_rep.exp ^ b_rep.exp) & sign_bit != 0; |
| | 315 | |
| | 316 | // Shift the significands to give us round, guard and sticky, and or in the |
| | 317 | // implicit significand bit. (If we fell through from the denormal path it |
| | 318 | // was already set by normalize( ), but setting it twice won't hurt |
| | 319 | // anything.) |
| | 320 | a_int = a_int << 3; |
| | 321 | b_int = b_int << 3; |
| | 322 | |
| | 323 | // Shift the significand of b by the difference in exponents, with a sticky |
| | 324 | // bottom bit to get rounding correct. |
| | 325 | const @"align" = @intCast(u80, a_exp - b_exp); |
| | 326 | if (@"align" != 0) { |
| | 327 | if (@"align" < 80) { |
| | 328 | const sticky = if (b_int << @intCast(u7, 80 - @"align") != 0) @as(u80, 1) else 0; |
| | 329 | b_int = (b_int >> @truncate(u7, @"align")) | sticky; |
| | 330 | } else { |
| | 331 | b_int = 1; // sticky; b is known to be non-zero. |
| | 332 | } |
| | 333 | } |
| | 334 | if (subtraction) { |
| | 335 | a_int -= b_int; |
| | 336 | // If a == -b, return +zero. |
| | 337 | if (a_int == 0) return 0.0; |
| | 338 | |
| | 339 | // If partial cancellation occurred, we need to left-shift the result |
| | 340 | // and adjust the exponent: |
| | 341 | if (a_int < int_bit << 3) { |
| | 342 | const shift = @intCast(i32, @clz(u80, a_int)) - @intCast(i32, @clz(u80, int_bit << 3)); |
| | 343 | a_int <<= @intCast(u7, shift); |
| | 344 | a_exp -= shift; |
| | 345 | } |
| | 346 | } else { // addition |
| | 347 | a_int += b_int; |
| | 348 | |
| | 349 | // If the addition carried up, we need to right-shift the result and |
| | 350 | // adjust the exponent: |
| | 351 | if (a_int & (int_bit << 4) != 0) { |
| | 352 | const sticky = a_int & 1; |
| | 353 | a_int = a_int >> 1 | sticky; |
| | 354 | a_exp += 1; |
| | 355 | } |
| | 356 | } |
| | 357 | |
| | 358 | // If we have overflowed the type, return +/- infinity: |
| | 359 | if (a_exp >= max_exp) { |
| | 360 | a_rep.exp = max_exp | result_sign; |
| | 361 | a_rep.fraction = int_bit; // integer bit is set for +/-inf |
| | 362 | return @ptrCast(*const f80, &a_rep).*; |
| | 363 | } |
| | 364 | |
| | 365 | if (a_exp <= 0) { |
| | 366 | // Result is denormal before rounding; the exponent is zero and we |
| | 367 | // need to shift the significand. |
| | 368 | const shift = @intCast(u80, 1 - a_exp); |
| | 369 | const sticky = if (a_int << @intCast(u7, 80 - shift) != 0) @as(u1, 1) else 0; |
| | 370 | a_int = a_int >> @intCast(u7, shift | sticky); |
| | 371 | a_exp = 0; |
| | 372 | } |
| | 373 | |
| | 374 | // Low three bits are round, guard, and sticky. |
| | 375 | const round_guard_sticky = @truncate(u3, a_int); |
| | 376 | |
| | 377 | // Shift the significand into place. |
| | 378 | a_int = @truncate(u64, a_int >> 3); |
| | 379 | |
| | 380 | // // Insert the exponent and sign. |
| | 381 | a_int |= (@intCast(u80, a_exp) | result_sign) << significand_bits; |
| | 382 | |
| | 383 | // Final rounding. The result may overflow to infinity, but that is the |
| | 384 | // correct result in that case. |
| | 385 | if (round_guard_sticky > 0x4) a_int += 1; |
| | 386 | if (round_guard_sticky == 0x4) a_int += a_int & 1; |
| | 387 | |
| | 388 | a_rep.fraction = @truncate(u64, a_int); |
| | 389 | a_rep.exp = @truncate(u16, a_int >> significand_bits); |
| | 390 | return @ptrCast(*const f80, &a_rep).*; |
| | 391 | } |
| | 392 | |
| | 393 | pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 { |
| | 394 | var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*; |
| | 395 | b_rep.exp ^= 0x8000; |
| | 396 | return __addxf3(a, @ptrCast(*const f80, &b_rep).*); |
| | 397 | } |
| | 398 | |
| 228 | test { | 399 | test { |
| 229 | _ = @import("addXf3_test.zig"); | 400 | _ = @import("addXf3_test.zig"); |
| 230 | } | 401 | } |