For an ERC20 token, number of decimals are defined in the contract deployed on the address of that token.
Example:
pragma solidity ^0.4.11;
import 'zeppelin/contracts/math/SafeMath.sol';
import 'zeppelin/contracts/token/ERC20.sol';
contract ExampleCoin is ERC20 {
using SafeMath for uint256;
string public symbol = "EXAMPLE";
string public name = "ExampleCoin";
uint8 public decimals = 18;
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
function ExampleCoin() public {
balances[msg.sender] = 1000 * (10 ** uint256(decimals));
totalSupply = 1000 * (10 ** uint256(decimals));
}
}
Every ERC20 contract follows same set of standards. All of them have a public decimal value (Thank you open Zeppelin)
All you need is to read that decimal value from the contract's address.
What is even more exciting, you can use generic ABI to connect to any ERC20 contract address and fetch its decimal value. See here
I hope this helps!