Writing powers of 10 as constants compactly
Asked Answered
C

2

7

I'm reading the recently released The Go Programming Language, and it's been a joy so far (with Brian Kernighan being one of the authors, I wouldn't expect anything other than excellence anyway).

I've come across the following exercise on chapter 3:

Exercise 3.13 Write const declarations for KB, MB, up through YB as compactly as you can.

(NOTE: in this context, KB, MB, etc, denote powers of 1000)

This is preceded by a section where iota is introduced as a useful constants generator mechanism; in particular, the previous paragraph shows a nice and compact way to define the powers of 1024 as constants:

const (
    _ = 1 << (10 * iota)
    KiB
    MiB
    GiB
    TiB
    PiB
    EiB
    ZiB
    YiB
)

The authors further mention this regarding powers of 10:

The iota mechanism has its limits. For example, it's not possible to generate the more familiar powers of 1000 (KB, MB, and so son) because there is no exponentiation operator.

I'm struggling with this exercise because it looks like the expected solution is something a little more elaborate than simply spelling out the powers of 1000 by hand (especially since it appears after iota is introduced). I feel like there is some clever way to do this that uses iota in a subtle way combined with something else.

I thought about finding a systematic way to subtract the "excess" amount out of each of the powers of 1024 to get the powers of 1000, but it led me to nowhere. Then I looked at the binary representations to try and infer a general pattern where iota could be useful, but again, I got nothing.

I really can't see how one would generate powers of 1000 out of a single incrementing value (iota) without an exponentiation operator.

Any ideas?

Crescentic answered 6/12, 2015 at 23:47 Comment(3)
Can you write a little bit more clear your problem? What exactly do you want to get?Mendive
@SalvadorDali Sorry if I wasn't clear. The question is basically how you would solve the exercise? Because it looks like I'm supposed to use iota in some way, given the context.Superposition
What about const ( KB = 1000; MB = 1000 * KB; GB = 1000 * MB; TB = 1000 * GB; /* ... */ )? Not as slick as the powers of 1024 using iota and implicit repetition, but I can't think of anything more compact if you want them to be constants.Kemp
M
3

I would say that this is impossible because what you want is to represent a function 10^(3i) where i is a positive integer as some function f(i), where f is a compositive function of your elementary go functions (+, -, /, *).

It was possible for 2^(10i) only because go introduced another elementary function integer exponentiation. So if 1 << y would allow y being float, you would be able to modify your code to use 1 << (log2(10) * 3 * i). This would worked because this is equivalent to solving 10^(3i) = 2^y. Taking log2 of both sides y = log2(10) * 3 * i.

But sadly enough bitwise shift is an integer operation.

Mendive answered 7/12, 2015 at 0:39 Comment(0)
S
11

You quoted it yourself:

The iota mechanism has its limits. For example, it's not possible to generate the more familiar powers of 1000 (KB, MB, and so son) because there is no exponentiation operator.

The authors don't want you to still find a way despite they're not knowing any. The authors want you to create constant declarations for KB, MB etc. as compact as you can.

With Floating-point literals

Here's a compact way. This utilizes Floating-point literals with exponent part. Think of it: writing 1e3 is even shorter than writing 1000 (not to mention the rest...).

Also it compresses all identifiers into one constant specification, so we reduce the = signs to 1.

Here it is, just one line (67 characters without spaces):

const ( KB, MB, GB, TB, PB, EB, ZB, YB = 1e3, 1e6, 1e9, 1e12, 1e15, 1e18, 1e21, 1e24 )

Note that since we used floating-point literals, the constant identifiers (KB, MB...) denote floating-point constants, even though the literals' fractional parts are zero.

With integer literal, using KB as the multiplier

If we want untyped integer constants, we have to write 1000 for the KB. And to get the next one, we would automatically turn to multiply the previous identifier with 1000. But note that we can also multiply the next one with KB because it's exactly 1000 - but shorter by two characters :).

And so here are the untyped integer constant declarations (77 characters without spaces):

const (KB,MB,GB,TB,PB,EB,ZB,YB = 1000,KB*KB,MB*KB,GB*KB,TB*GB,PB*KB,EB*KB,ZB*KB)

(Sorry for removing spaces, but wanted it to fit in one line.)

With integer literal, using an extra x const as the multiplier

You can even gain 3 characters from the last solution if you also introduce a 1-char length const x which you use multiple times to do the multiplication instead of the *KB:

With an extra x const (74 characters without spaces):

const (x,KB,MB,GB,TB,PB,EB,ZB,YB = 1000,x,x*x,MB*x,GB*x,TB*GB,PB*x,EB*x,ZB*x)

With rune literal

We can even shorten it one more character if we specify the 1000 constant as a rune constant, with a rune whose code point is 1000, which is 'Ϩ' - which is 1 character less :)

With a rune literal 'Ϩ' const (73 characters without spaces):

const (x,KB,MB,GB,TB,PB,EB,ZB,YB = 'Ϩ',x,x*x,MB*x,GB*x,TB*GB,PB*x,EB*x,ZB*x)

Note that these will be rune constants, but just as all other numeric constants, they represent values of arbitrary precision and do not overflow.

Spinach answered 8/12, 2015 at 0:0 Comment(2)
Thanks, I do believe there is no "clever" way with iota, especially after Salvador's answer. I appreciate you taking the time to answer my question but unfortunately I can only accept one answer. I like the floating-point approach, but I prefer the other where you multiply by the previous - it's clearer and it doesn't yield constants where the underlying type is a float.Superposition
@FilipeGonçalves the underlying types must be floats, ZB and YB will overflow uint64. What's interesting is that the const declarations will be fine, but your code will fail once you try to use them. Golang is funny about types of constants: blog.golang.org/constantsKemp
M
3

I would say that this is impossible because what you want is to represent a function 10^(3i) where i is a positive integer as some function f(i), where f is a compositive function of your elementary go functions (+, -, /, *).

It was possible for 2^(10i) only because go introduced another elementary function integer exponentiation. So if 1 << y would allow y being float, you would be able to modify your code to use 1 << (log2(10) * 3 * i). This would worked because this is equivalent to solving 10^(3i) = 2^y. Taking log2 of both sides y = log2(10) * 3 * i.

But sadly enough bitwise shift is an integer operation.

Mendive answered 7/12, 2015 at 0:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.