Skip to content

Commit 4412181

Browse files
committed
math/big: normalize unitialized denominators ASAP
A Rat is represented via a quotient a/b where a and b are Int values. To make it possible to use an uninitialized Rat value (with a and b uninitialized and thus == 0), the implementation treats a 0 denominator as 1. For each operation we check if the denominator is 0, and then treat it as 1 (if necessary). Operations that create a new Rat result, normalize that value such that a result denominator 1 is represened as 0 again. This CL changes this behavior slightly: 0 denominators are still interpreted as 1, but whenever we (safely) can, we set an uninitialized 0 denominator to 1. This simplifies the code overall. Also: Improved some doc strings. Preparation for addressing issue #33792. Updates #33792. Change-Id: I3040587c8d0dad2e840022f96ca027d8470878a0 Reviewed-on: https://go-review.googlesource.com/c/go/+/202997 Run-TryBot: Robert Griesemer <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 758eb02 commit 4412181

File tree

1 file changed

+28
-27
lines changed

1 file changed

+28
-27
lines changed

src/math/big/rat.go

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ import (
2222
// of Rats are not supported and may lead to errors.
2323
type Rat struct {
2424
// To make zero values for Rat work w/o initialization,
25-
// a zero value of b (len(b) == 0) acts like b == 1.
25+
// a zero value of b (len(b) == 0) acts like b == 1. At
26+
// the earliest opportunity (when an assignment to the Rat
27+
// is made), such uninitialized denominators are set to 1.
2628
// a.neg determines the sign of the Rat, b.neg is ignored.
2729
a, b Int
2830
}
@@ -297,6 +299,7 @@ func (x *Rat) Float64() (f float64, exact bool) {
297299
}
298300

299301
// SetFrac sets z to a/b and returns z.
302+
// If b == 0, SetFrac panics.
300303
func (z *Rat) SetFrac(a, b *Int) *Rat {
301304
z.a.neg = a.neg != b.neg
302305
babs := b.abs
@@ -312,11 +315,12 @@ func (z *Rat) SetFrac(a, b *Int) *Rat {
312315
}
313316

314317
// SetFrac64 sets z to a/b and returns z.
318+
// If b == 0, SetFrac64 panics.
315319
func (z *Rat) SetFrac64(a, b int64) *Rat {
316-
z.a.SetInt64(a)
317320
if b == 0 {
318321
panic("division by zero")
319322
}
323+
z.a.SetInt64(a)
320324
if b < 0 {
321325
b = -b
322326
z.a.neg = !z.a.neg
@@ -328,21 +332,21 @@ func (z *Rat) SetFrac64(a, b int64) *Rat {
328332
// SetInt sets z to x (by making a copy of x) and returns z.
329333
func (z *Rat) SetInt(x *Int) *Rat {
330334
z.a.Set(x)
331-
z.b.abs = z.b.abs[:0]
335+
z.b.abs = z.b.abs.setWord(1)
332336
return z
333337
}
334338

335339
// SetInt64 sets z to x and returns z.
336340
func (z *Rat) SetInt64(x int64) *Rat {
337341
z.a.SetInt64(x)
338-
z.b.abs = z.b.abs[:0]
342+
z.b.abs = z.b.abs.setWord(1)
339343
return z
340344
}
341345

342346
// SetUint64 sets z to x and returns z.
343347
func (z *Rat) SetUint64(x uint64) *Rat {
344348
z.a.SetUint64(x)
345-
z.b.abs = z.b.abs[:0]
349+
z.b.abs = z.b.abs.setWord(1)
346350
return z
347351
}
348352

@@ -352,6 +356,9 @@ func (z *Rat) Set(x *Rat) *Rat {
352356
z.a.Set(&x.a)
353357
z.b.Set(&x.b)
354358
}
359+
if len(z.b.abs) == 0 {
360+
z.b.abs = z.b.abs.setWord(1)
361+
}
355362
return z
356363
}
357364

@@ -370,20 +377,13 @@ func (z *Rat) Neg(x *Rat) *Rat {
370377
}
371378

372379
// Inv sets z to 1/x and returns z.
380+
// If x == 0, Inv panics.
373381
func (z *Rat) Inv(x *Rat) *Rat {
374382
if len(x.a.abs) == 0 {
375383
panic("division by zero")
376384
}
377385
z.Set(x)
378-
a := z.b.abs
379-
if len(a) == 0 {
380-
a = a.set(natOne) // materialize numerator (a is part of z!)
381-
}
382-
b := z.a.abs
383-
if b.cmp(natOne) == 0 {
384-
b = b[:0] // normalize denominator
385-
}
386-
z.a.abs, z.b.abs = a, b // sign doesn't change
386+
z.a.abs, z.b.abs = z.b.abs, z.a.abs
387387
return z
388388
}
389389

@@ -426,25 +426,20 @@ func (x *Rat) Denom() *Int {
426426
func (z *Rat) norm() *Rat {
427427
switch {
428428
case len(z.a.abs) == 0:
429-
// z == 0 - normalize sign and denominator
429+
// z == 0; normalize sign and denominator
430430
z.a.neg = false
431-
z.b.abs = z.b.abs[:0]
431+
fallthrough
432432
case len(z.b.abs) == 0:
433-
// z is normalized int - nothing to do
434-
case z.b.abs.cmp(natOne) == 0:
435-
// z is int - normalize denominator
436-
z.b.abs = z.b.abs[:0]
433+
// z is integer; normalize denominator
434+
z.b.abs = z.b.abs.setWord(1)
437435
default:
436+
// z is fraction; normalize numerator and denominator
438437
neg := z.a.neg
439438
z.a.neg = false
440439
z.b.neg = false
441440
if f := NewInt(0).lehmerGCD(nil, nil, &z.a, &z.b); f.Cmp(intOne) != 0 {
442441
z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f.abs)
443442
z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs)
444-
if z.b.abs.cmp(natOne) == 0 {
445-
// z is int - normalize denominator
446-
z.b.abs = z.b.abs[:0]
447-
}
448443
}
449444
z.a.neg = neg
450445
}
@@ -456,6 +451,8 @@ func (z *Rat) norm() *Rat {
456451
// returns z.
457452
func mulDenom(z, x, y nat) nat {
458453
switch {
454+
case len(x) == 0 && len(y) == 0:
455+
return z.setWord(1)
459456
case len(x) == 0:
460457
return z.set(y)
461458
case len(y) == 0:
@@ -511,10 +508,14 @@ func (z *Rat) Sub(x, y *Rat) *Rat {
511508
// Mul sets z to the product x*y and returns z.
512509
func (z *Rat) Mul(x, y *Rat) *Rat {
513510
if x == y {
514-
// a squared Rat is positive and can't be reduced
511+
// a squared Rat is positive and can't be reduced (no need to call norm())
515512
z.a.neg = false
516513
z.a.abs = z.a.abs.sqr(x.a.abs)
517-
z.b.abs = z.b.abs.sqr(x.b.abs)
514+
if len(x.b.abs) == 0 {
515+
z.b.abs = z.b.abs.setWord(1)
516+
} else {
517+
z.b.abs = z.b.abs.sqr(x.b.abs)
518+
}
518519
return z
519520
}
520521
z.a.Mul(&x.a, &y.a)
@@ -523,7 +524,7 @@ func (z *Rat) Mul(x, y *Rat) *Rat {
523524
}
524525

525526
// Quo sets z to the quotient x/y and returns z.
526-
// If y == 0, a division-by-zero run-time panic occurs.
527+
// If y == 0, Quo panics.
527528
func (z *Rat) Quo(x, y *Rat) *Rat {
528529
if len(y.a.abs) == 0 {
529530
panic("division by zero")

0 commit comments

Comments
 (0)