0
p=n/1000
printf "%s",arr[p];

This isnt printing anything although I have checked the value of p.
It is coming out alright.
I have tried printing the element this way as well:

printf "%s",arr[7];

This one works.

6
  • Hello and welcome on SO. I have editing your question formatting. This is not plain text, but markdown rendering. Now, if you need people to answer you, please consider helping them by adapting your question to this website community style. You will find help there.
    – iago-lito
    Commented Sep 8, 2018 at 21:15
  • Ok. Thanks for the suggestion. I will look into it
    – Rishav
    Commented Sep 8, 2018 at 21:17
  • 1
    What should it print? You're not showing how arr is set, or n either for that matter. Edit your question to include a complete runnable program so that there's something concrete to look into.
    – ilkkachu
    Commented Sep 8, 2018 at 21:17
  • The array contains strings. I am converting a number into words. N is that number.
    – Rishav
    Commented Sep 8, 2018 at 21:19
  • 1
    @Rishav, no, that's not a complete program. Your question says you have checked the value of p, but obviously it doesn't work. There must be some else in there that's wrong. So, please show a complete program that exhibits the issue. Otherwise there's no way to be sure what the problem is. Also, please edit your question to add the code or any other details you may have.
    – ilkkachu
    Commented Sep 8, 2018 at 22:00

2 Answers 2

2

As noted in The GNU Awk Users' Guide 8.2 Using Numbers to Subscript Arrays

An important aspect to remember about arrays is that array subscripts are always strings. When a numeric value is used as a subscript, it is converted to a string value before being used for subscripting

If n is 7432 then n/1000 is 7.432 - it won't be rounded by default. So

$ awk -vn=7432 'BEGIN{arr[7] = "seven"; arr["7.432"] = "foo"; p = n/1000; print p, arr[p]}'
7.432 foo

whereas

$ awk -vn=7432 'BEGIN{arr[7] = "seven"; arr["7.432"] = "foo"; p = int(n/1000); print p, arr[p]}'
7 seven
1
  • Thank you so much man. It really worked. I was printing p with %d so I thought it was being calculated alright.
    – Rishav
    Commented Sep 9, 2018 at 3:35
2

A complement to @steeldriver's fine answer.

7432/1000 is not an integer. awk internally use the C compiler's double type to represent floating point numbers. On GNU systems on x86, that's IEEE 754 binary64.

Just like 1/3 can't be represented in decimal (that's 0.33333 with an infinite number of 3s), 7432/1000 cannot be represented in binary. awk's double approximation of that number is a binary number which when converted back to decimal is (exactly) 7.4320000000000003836930773104541003704071044921875.

Now, in awk, when non-integer numbers are converted to a string representation, the exact value of the binary internal representation is not fully preserved. That would not be very useful given that anyway most of those digits above actually represent an error. Instead floating point numbers are converted back to string using the CONVFMT special parameter which contains a printf-like format¹

By default CONVFMT is %.6g. Which means only 6 decimal digits of precision are retained upon conversion to string. That can be changed to anything though note that only floating point specifiers (%e, %f, %g...) are portable.

Those numbers are converted to string when they are used as argument to functions or operators that expect strings, like substr(), concatenation...

One of those as @steeldriver said are keys to associative arrays which are always strings.

So doing a[7432/1000] = x is actually doing a[sprintf(CONVFMT, 7432/1000)]. With the default value of CONVFMT, that becomes a["0.7432"] = x but if CONVFMT was changed to %.1e, that would be a["7.0e-01"] = x, or if it was %.25g, that would be a["7.4320000000000003837"] = x.

So here you could actually use CONVFMT=%.0f to make sure keys of the array are integers:

$ awk -v CONVFMT=%.0f 'BEGIN{a[7432/1000] = 1; print a[7]}'
1

Note however that %.0f rounds to the nearest integer while %d truncates the decimal part. Using CONVFMT=%d is not portable though would work with gawk and mawk:

$ gawk -v CONVFMT=%d 'BEGIN{a[7564/1000] = 1; print a[7]}'
1
$ gawk -v CONVFMT=%.0f 'BEGIN{a[7564/1000] = 1; print a[8]}'
1

¹ that doesn't cover floating point arguments to print which are are converted using OFMT instead

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .