Version 4 of Nice Numbers

Updated 2006-09-01 12:44:34

KPV I was recently searching this wiki for routines for drawing charts when I saw in two different places two similar, but very clever, routines that I thought deserved a page to themselves.

They both solve the problem for graphs of determining the limits of an axis so that it encompasses the range of values of the graph but also begin and end on Nice Numbers. For example, with a range of, say, 5-86 you'd like the axis to range from 0-100.

The first algorithm is from Chart generation support by Dave Griffin.

 #
 # nice_number
 #
 #   Reference: Paul Heckbert, "Nice Numbers for Graph Labels",
 #          Graphics Gems, pp 61-63.
 #
 #   Finds a "nice" number approximately equal to x.
 #
 #   Args: x -- target number
 #         round -- If non-zero, round. Otherwise take ceiling of value.

 proc nice_number {x {round 0}} {

    #   expt -- Exponent of x
    #   frac -- Fractional part of x
    #   nice -- Nice, rounded fraction

    set expt [expr {floor(log10($x))}]
    set frac [expr {$x / pow(10.0, double($expt))}]
    if ($round) {
        if {$frac < 1.5} {
            set nice 1.0
        } elseif {$frac < 3.0} {
            set nice 2.0
        } elseif {$frac < 7.0} {
            set nice 5.0
        } else {
            set nice 10.0
        }
    } else {
        if {$frac <= 1.0} {
            set nice  1.0
        } elseif {$frac <= 2.0} {
            set nice  2.0
        } elseif {$frac <= 5.0} {
            set nice 5.0
        } else {
            set nice 10.0
        }
    }
   return [expr {$nice * pow(10.0, double($expt))}]
 }

if 0 { The second routine is from A little bar chart by the ubiquitous Richard Suchenwirth: }

 # An interesting sub-challenge was to round numbers very roughly,
 # to 1 or maximally 2 significant digits - by default rounding up,
 # add "-" to round down:}
 proc Roughly {n {sgn +}} {
    regexp {(.+)e([+-])0*(.+)} [format %e $n] -> mant sign exp
    set exp [expr $sign$exp]
    if {abs($mant)<1.5} {
        set mant [expr {$mant*10}]
        incr exp -1
    }
    set t [expr round($mant $sgn 0.49)*pow(10,$exp)]
    expr {$exp>=0? int($t): $t}
 }


 # Test:

 catch {console show}
 catch {wm withdraw .}

 foreach x {7 11 22 33 44 77} {
   puts "$x nice_number: [nice_number $x]"
   puts "$x Roughly    : [Roughly     $x] \n"
 }

HJG Test added.


Category Mathematics